---
title: "Scopes"
description: "A scope holds the data the SDK attaches to your events. Learn how the Native SDK's global scope works, and when to create a scope of your own."
url: https://docs.sentry.io/platforms/native/enriching-events/scopes/
---

# Scopes | Sentry for Native

Scopes store extra data that the SDK adds to your event when sending the event to Sentry. Some of it you set yourself and some the SDK fills in, so it's worth knowing what a scope holds and how one gets applied.

## [What's a Scope](https://docs.sentry.io/platforms/native/enriching-events/scopes.md#whats-a-scope)

A scope holds the data that rides along with your telemetry: [tags](https://docs.sentry.io/platforms/native/enriching-events/tags.md), [contexts](https://docs.sentry.io/platforms/native/enriching-events/context.md), [breadcrumbs](https://docs.sentry.io/platforms/native/enriching-events/breadcrumbs.md), [user](https://docs.sentry.io/platforms/native/enriching-events/identify-user.md), severity level, fingerprint, [attachments](https://docs.sentry.io/platforms/native/enriching-events/attachments.md), and [attributes](https://docs.sentry.io/platforms/native/enriching-events/attributes.md) carried by [logs](https://docs.sentry.io/platforms/native/logs.md) and [metrics](https://docs.sentry.io/platforms/native/metrics.md). Each has a `sentry_scope_*` setter taking the scope as its first argument, alongside the `sentry_set_*` function that writes the same data globally.

Two scopes can apply to a capture: the global scope, which the SDK creates during `sentry_init()` and applies to everything it sends, and, optionally, a scope you create and hand to a capture function. A scope you create doesn't replace the global scope, it layers on top of it, so the event still gets the global scope's release, environment, tags, and user.

##### Crash Events Only Carry the Global Scope

A scope you create reaches Sentry only through the `sentry_scope_capture_*` functions. Crash events don't use it because, at crash time, the SDK can only rely on the global scope.

Put anything a crash report needs — a build identifier, the signed-in user, or the asset being processed — on the global scope.

## [The Global Scope](https://docs.sentry.io/platforms/native/enriching-events/scopes.md#the-global-scope)

The global scope applies to every event, log, metric, and crash, so it's where long-lived data belongs — the signed-in user, the build channel, a machine identifier. The SDK owns it: it's created during `sentry_init()` and released by `sentry_close()`.

```c
#include <sentry.h>

sentry_set_tag("my-tag", "my value");

// Set a user with id, username, email, and ip_address
sentry_value_t user = sentry_value_new_user("42", "Jane Doe", "jane.doe@example.com", "{{auto}}");
sentry_set_user(user);
```

You can also set data on scopes you create, using the `sentry_scope_*` setters that take the scope as their first argument:

```c
sentry_scope_set_tag(scope, "my-tag", "my value");
sentry_scope_set_user(scope, user);
```

Alongside tags and users, scopes accept breadcrumbs, contexts, extras, levels, fingerprints, attributes, and attachments:

```c
sentry_scope_add_breadcrumb(scope, breadcrumb);
sentry_scope_set_context(scope, "character", character);
sentry_scope_update_context(scope, "character", partial_character);
sentry_scope_set_extra(scope, "character.name", name);
sentry_scope_set_level(scope, SENTRY_LEVEL_WARNING);
sentry_scope_set_fingerprint(scope, "my-fingerprint", NULL);
sentry_scope_set_attribute(scope, "cache.backend", attribute);
sentry_scope_attach_file(scope, "/var/local.log");
```

The ones taking a string key or path also have an `_n` variant that takes explicit lengths, such as `sentry_scope_set_tag_n` and `sentry_scope_attach_file_n`.

##### One Global Scope, Shared by All Threads

The Native SDK keeps a single global scope shared by every thread, and no thread-local state. Writes to it are safe from a data-race perspective, but they aren't isolated: a tag one thread sets is visible to events captured on every other thread.

If your program handles several users or requests concurrently, capture with a scope per unit of work instead.

For how this affects tracing, see [Connect Errors With Spans](https://docs.sentry.io/platforms/native/tracing/instrumentation/custom-instrumentation.md#connect-errors-with-spans).

## [Creating a Scope](https://docs.sentry.io/platforms/native/enriching-events/scopes.md#creating-a-scope)

Create a scope when the data describes a unit of work rather than the whole program, such as the asset being decoded or the request being handled. Set the data on the scope, then pass it to a capture function.

The constructor you choose determines the scope's lifetime. Use `sentry_local_scope_new` for a one-shot scope when the data applies to a single capture:

```c
sentry_scope_t *scope = sentry_local_scope_new();
sentry_scope_set_tag(scope, "asset.kind", "texture");
sentry_scope_attach_file(scope, "/var/log/asset-loader.log");

sentry_value_t event = sentry_value_new_message_event(
    SENTRY_LEVEL_ERROR, "asset-loader", "Failed to decode texture");

// Takes ownership of the scope and frees it.
sentry_scope_capture_event(scope, event);
```

`sentry_scope_new` creates a scope you own. Configure it once and reuse it for as many captures as you need when the same context would otherwise be rebuilt each time, such as for a worker thread, subsystem, or connected client:

```c
sentry_scope_t *scope = sentry_scope_new();
sentry_scope_set_tag(scope, "worker", "asset-loader");
sentry_scope_set_user(scope, user);

// Applied, but not freed.
sentry_scope_capture_event(scope, first_event);
sentry_scope_capture_event(scope, second_event);

sentry_scope_free(scope);
```

Lifetime is the only thing that separates the two. Both are a `sentry_scope_t *` and take the same setters, so the constructor you chose is the only thing that decides who cleans up, and getting it wrong means either a double free or a leak. Every `sentry_scope_capture_*` function follows this rule.

## [Capturing With a Scope](https://docs.sentry.io/platforms/native/enriching-events/scopes.md#capturing-with-a-scope)

Scope data isn't just for errors. Feedback, logs, and metrics each can take a scope through their own `sentry_scope_capture_*` function, so the context you build for one capture can enrich any of them:

```c
sentry_scope_capture_event(scope, event);
sentry_scope_capture_feedback(scope, feedback, hint);
sentry_scope_capture_log(scope, SENTRY_LEVEL_INFO, "Cache miss", attributes);
sentry_scope_capture_metric(
    scope, SENTRY_METRIC_COUNT, "cache.miss", sentry_value_new_int64(1), NULL, attributes);
```

## [How Scope Data Is Applied](https://docs.sentry.io/platforms/native/enriching-events/scopes.md#how-scope-data-is-applied)

When you capture with a scope, the SDK combines that scope with the global scope. Data set in only one place is still included, so events captured with their own scope continue to carry global values such as release, environment, and user. When the same key is set in more than one place, the most specific one wins: **event data** > **passed scope** > **global scope**. Data you set directly on the event value therefore survives whatever the scopes carry.

Logs and metrics resolve attributes and the trace from the call site as well, which is the most specific source of the three: **per-call attributes** > **passed scope** > **global scope**. See [Attributes](https://docs.sentry.io/platforms/native/enriching-events/attributes.md) for how to build them.

## [Clearing and Copying Scopes](https://docs.sentry.io/platforms/native/enriching-events/scopes.md#clearing-and-copying-scopes)

To clear a scope, use `sentry_scope_clear` to discard most data while preserving the trace:

```c
sentry_scope_clear(scope); // Ready for the next request.
```

`sentry_scope_clone` copies a scope so you can branch off a configured base. Top-level fields are copied and nested values are shared by reference. The copy is always one you own, even when cloned from a scope created with `sentry_local_scope_new`, so you must free it yourself:

```c
sentry_scope_t *request_scope = sentry_scope_clone(base_scope);
sentry_scope_set_tag(request_scope, "request.id", request_id);

sentry_scope_capture_event(request_scope, event);
sentry_scope_free(request_scope);
```

To learn what else you can put on a scope, see [the context documentation](https://docs.sentry.io/platforms/native/enriching-events/context.md).
