---
title: "Scopes"
description: "The three-scope model for propagating contextual data through SDK execution — global, isolation, and current scopes."
url: https://develop.sentry.dev/sdk/foundations/state-management/scopes/
---

# Scopes

This document uses key words such as "MUST", "SHOULD", and "MAY" as defined in

<!-- -->

[RFC 2119](https://www.ietf.org/rfc/rfc2119.txt) to indicate requirement levels.

Statusstable

Version`1.15.0`[(changelog)](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#changelog)

## [Overview](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#overview)

Scopes are the mechanism by which Sentry SDKs propagate contextual data (tags, breadcrumbs, user, contexts, attributes) through an application and apply it to captured events. Every SDK **MUST** implement three scope types — global, isolation, and current — each with a different propagation lifetime.

This model replaces the [Hub & Scope](https://develop.sentry.dev/sdk/foundations/state-management/hub-and-scope.md) model. The Hub is removed; its functionality is absorbed into the three scope types. The design aligns with [OpenTelemetry's Context](https://opentelemetry.io/docs/specs/otel/context/) propagation — an immutable, fork-on-write mechanism that carries execution-scoped values across API boundaries. See [RFC 0122](https://github.com/getsentry/rfcs/pull/122) for the original design.

Related specs:

* [Attributes](https://develop.sentry.dev/sdk/foundations/state-management/scopes/attributes.md) — attribute type system for scope data
* [Breadcrumbs](https://develop.sentry.dev/sdk/foundations/state-management/scopes/breadcrumbs.md) — structured event trail on scopes
* [Hub & Scope](https://develop.sentry.dev/sdk/foundations/state-management/hub-and-scope.md) — deprecated predecessor

***

## [Concepts](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#concepts)

### [Three Scope Types](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#three-scope-types)

| Scope         | Lifetime                         | Storage                                       | Who Forks It                         |
| ------------- | -------------------------------- | --------------------------------------------- | ------------------------------------ |
| **Global**    | Entire process                   | Global variable                               | Never forked                         |
| **Isolation** | Request, tab, or user session    | Context variable / thread-local / async-local | SDK integrations (automatically)     |
| **Current**   | Single span or `withScope` block | Context variable / thread-local / async-local | `withScope()` or starting a new span |

### [Copy-on-Write](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#copy-on-write)

When a scope is forked, the new scope receives a copy of all data from the parent. Changes to the forked scope do not affect the parent, and changes to the parent after forking do not affect the fork.

This replaces the manual hub-cloning pattern from the old model. Users no longer need to manage scope isolation — it happens automatically when spans are created or `withScope()` is called.

### [Automatic Context Data](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#automatic-context-data)

SDKs and their integrations automatically populate scopes with useful contextual data such as tags, contexts, and attributes. This typically happens by hooking into a framework (e.g., a web framework middleware setting request-related context on the isolation scope). This automatic enrichment is a key benefit of scopes — users get useful data on events without manual instrumentation. See [Data Scrubbing](https://develop.sentry.dev/sdk/foundations/data-scrubbing.md) for considerations around sensitive data.

### [Scope Chain](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#scope-chain)

Clients are resolved by walking the scope chain: current scope -> isolation scope -> global scope. If no client is found, a NoOpClient is used (see [Client Resolution](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#client-resolution)).

***

## [Behavior](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#behavior)

### [Global Scope](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#global-scope)

Stablespecified since

<!-- -->

1.0.0

The global scope is a process-wide singleton. Data set on the global scope **MUST** be applied to **all** events emitted by the SDK, regardless of thread, request, or execution context.

The global scope is typically used for application-wide data such as `release` and `environment`.

The global scope is **never** forked. There is exactly one global scope per process.

Before `Sentry.init()` is called, the global scope **MUST** exist with a NoOpClient. `Sentry.init()` replaces the NoOpClient with the configured client on the global scope.

### [Isolation Scope](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#isolation-scope)

Stablespecified since

<!-- -->

1.0.0

The isolation scope **MUST** contain data specific to the current execution context: a single request (on a server), a single tab (in a browser), or a single user session (on mobile).

Top-level SDK APIs such as `Sentry.setTag()`, `Sentry.setUser()`, and `Sentry.setContext()` **MUST** write to the isolation scope.

The isolation scope **SHOULD** be implemented using a context variable, thread-local storage, async-local storage, or an equivalent mechanism appropriate for the platform.

SDK integrations **MUST** handle the forking of isolation scopes automatically. Users **MUST NOT** need to manage or think about isolation scope forking. SDKs **SHOULD NOT** fork isolation scopes more than necessary — excessive forking reintroduces the problems isolation scopes are designed to solve.

When the isolation scope is forked, the SDK **MUST** also fork the current scope at the same time. This avoids requiring users to call both `withScope` and `withIsolationScope`.

**When to fork isolation scope:**

SDKs **SHOULD** fork the isolation scope at natural isolation boundaries:

* Incoming HTTP request (server)
* New tab or navigation (browser)
* New user session (mobile)
* Queue job or background task
* In POTel SDKs, `Propagator.extract` is a natural fork point (see [OTel Context Alignment](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#otel-context-alignment))

### [Current Scope](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#current-scope)

Stablespecified since

<!-- -->

1.0.0

The current scope **MUST** maintain data for the active span. When a new span is started, the current scope of the parent span **MUST** be forked (duplicated), transferring all data from the parent scope to the new scope. This allows modifications specific to the new span without affecting the parent span.

Any changes made to the current scope after forking **MUST NOT** impact the parent scope. Any changes made to the parent scope after forking **MUST NOT** impact the forked scope.

The current scope **SHOULD** be implemented using a context variable, thread-local storage, async-local storage, or an equivalent mechanism appropriate for the platform.

Users **MAY** fork the current scope explicitly by invoking `Sentry.withScope()` or implicitly by starting a new span.

### [Applying Scope Data to Events](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#applying-scope-data-to-events)

Stablespecified since

<!-- -->

1.0.0

Data from all three scope types **MUST** be merged in a specific order before being applied to an event:

1. Data from the **global scope** is...
2. merged with data from the **isolation scope**, which is...
3. merged with data from the **current scope**, which is...
4. optionally merged with additional data (e.g., from `captureException(error, { tags })`)...
5. applied to the **event**

When the same key exists in multiple scopes, the more specific scope wins: current > isolation > global. Event-level data (from step 4) takes highest precedence.

### [Copy-on-Write Semantics](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#copy-on-write-semantics)

Stablespecified since

<!-- -->

1.3.0

Scope forking implements copy-on-write:

* `withScope(callback)` forks the current scope for the duration of the callback. The forked scope is discarded when the callback returns.
* Starting a new span forks the current scope automatically.
* `withIsolationScope(callback)` forks both the isolation scope and the current scope.

After forking, the original and forked scopes are fully independent. Mutations to either do not affect the other.

This replaces the old model where users had to manually create hub clones to achieve isolation in async contexts. The new model handles this automatically — when OTel or the SDK forks a context (e.g., for a new span), the scope is forked with it.

### [Scope Attributes](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#scope-attributes)

Stablespecified since

<!-- -->

1.10.0

Users **MUST** be able to attach [attributes](https://develop.sentry.dev/sdk/foundations/state-management/scopes/attributes.md) to any scope. Attributes set on scopes follow scope precedence rules (current > isolation > global) and are applied to telemetry items at capture time. See the [Attributes](https://develop.sentry.dev/sdk/foundations/state-management/scopes/attributes.md) spec for the full data model, API, precedence rules, and examples. (since 1.13.0)

### [Scope Breadcrumbs](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#scope-breadcrumbs)

Stablespecified since

<!-- -->

1.0.0

Breadcrumbs live on scopes and create a trail of events leading up to an issue. They are stored in a ring buffer and merged from all three scopes at capture time. See the [Breadcrumbs](https://develop.sentry.dev/sdk/foundations/state-management/scopes/breadcrumbs.md) spec for the full data model, types, ring buffer behavior, and automatic recording.

### [Scope Data Methods](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#scope-data-methods)

Stablespecified since

<!-- -->

1.0.0

In addition to [attributes](https://develop.sentry.dev/sdk/foundations/state-management/scopes/attributes.md) and [breadcrumbs](https://develop.sentry.dev/sdk/foundations/state-management/scopes/breadcrumbs.md), SDKs **MUST** allow users to set user context, string tags, structured contexts, severity level, grouping fingerprints, and event processor callbacks on any scope. SDKs **MUST** also provide a method to reset a scope to its defaults while keeping registered event processors.

These methods have the same semantics as in the [Hub & Scope](https://develop.sentry.dev/sdk/foundations/state-management/hub-and-scope.md) spec. The scope type determines the propagation lifetime of the data.

### [Client Resolution](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#client-resolution)

Stablespecified since

<!-- -->

1.0.0

A client **MUST** always be available — SDKs **MUST NOT** return `null` from `getClient()`.

Before `Sentry.init()` is called, a NoOpClient **MUST** be present on the global scope. `Sentry.init()` replaces it with the configured client.

Users **MAY** bind a different client to any of the three scopes. Client resolution walks the scope chain:

1. Check the current scope for a client
2. Check the isolation scope for a client
3. Check the global scope for a client
4. Fall back to the NoOpClient (should not happen if init was called)

### [OTel Context Alignment](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#otel-context-alignment)

Stablespecified since

<!-- -->

1.4.0

The three-scope model is designed to align with OpenTelemetry's [Context](https://opentelemetry.io/docs/specs/otel/context/) propagation. OTel Context is immutable — mutations create a new Context (fork). Sentry scopes mirror this behavior.

SDKs that use OTel for instrumentation (POTel) **SHOULD** store scopes on the OTel Context to leverage OTel's propagation mechanisms (thread propagation, async propagation, reactive library support).

#### [Storing Scopes](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#storing-scopes)

SDKs **SHOULD** store both isolation and current scope on the OTel Context. This allows the SDK to rely on OTel's context propagation instead of maintaining its own propagation for each platform.

If the language allows modifying OTel spans (e.g., adding attributes or references), the SDK **MAY** store scope references directly on spans. Otherwise, a global weak-reference map from OTel spans to Sentry scopes **SHOULD** be used to avoid memory leaks.

#### [Forking Hooks](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#forking-hooks)

SDKs need a hook to fork scopes when OTel creates new spans or contexts. Available hooks vary by language:

* **Context forking**: Intercept `context.with(...)` calls
* **Context storing**: Intercept `context.makeCurrent()` calls
* **Span creation**: Intercept new span creation in `SpanProcessor.onStart`

SDKs **SHOULD** fork scopes when a new OTel span is created. If multiple hooks are available, SDKs **MAY** filter to only fork when the span changes, to reduce unnecessary forks.

#### [Isolation Scope in POTel](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#isolation-scope-in-potel)

SDKs **SHOULD** fork the isolation scope in `Propagator.extract`, which is called by auto-instrumentation for incoming server requests and message consumers. If this approach does not work for a given platform, the SDK **MAY** check `span.isRemote` to determine if a new isolation scope is needed.

#### [SpanProcessor.onEnd Limitation](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#spanprocessoronend-limitation)

In `SpanProcessor.onEnd` and `SpanExporter.export`, `Context.current()` returns the **parent** context, not the context that was active during span execution. This is by design in OTel. SDKs **MUST** retrieve the scope associated with a span via the span itself (if the language supports it) or via the global weak-reference map — not from `Context.current()`.

### [Propagation Context](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#propagation-context)

Stablespecified since

<!-- -->

1.0.0

The propagation context (trace ID, span ID, baggage/DSC) **MUST** be stored on the scope. It is used for:

* Generating `sentry-trace` and `baggage` headers for outgoing requests
* Connecting error events to the active trace (tracing without performance)

The isolation scope is the canonical location for the propagation context. When no active span exists, the propagation context from the isolation scope **MUST** be used. See [Trace Propagation: Default Propagation](https://develop.sentry.dev/sdk/foundations/trace-propagation.md#default-propagation) for details.

### [Backwards Compatibility](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#backwards-compatibility)

Stablespecified since

<!-- -->

1.2.0

During the migration from [Hub & Scope](https://develop.sentry.dev/sdk/foundations/state-management/hub-and-scope.md) to the three-scope model:

1. **Top-level APIs** (e.g., `Sentry.setTag()`) **SHOULD** write to **both** current and isolation scope during the migration phase. In a subsequent major version, they **MUST** write to isolation scope only.

2. **`getCurrentHub()`** **MAY** be provided as a compatibility shim that proxies to the new scope APIs. It **SHOULD** be marked deprecated.

3. **`configureScope(callback)`** **SHOULD** be deprecated. Users should migrate to calling `getIsolationScope()` or `getCurrentScope()` directly, depending on the intended scope lifetime.

4. **Hub-specific APIs** (`pushScope`, `popScope`, `bindClient`, `run`) **SHOULD** be shimmed to the new equivalents and marked deprecated.

See [Hub & Scope: Migration](https://develop.sentry.dev/sdk/foundations/state-management/hub-and-scope.md#migration) for the complete API mapping table.

***

## [Public API](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#public-api)

SDKs **MUST** expose the following top-level functions for scope access and manipulation:

### [Scope Accessors](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#scope-accessors)

| Function              | Since | Description                                                         |
| --------------------- | ----- | ------------------------------------------------------------------- |
| `getGlobalScope()`    | 1.0.0 | Return the global scope singleton.                                  |
| `getIsolationScope()` | 1.0.0 | Return the isolation scope for the current execution context.       |
| `getCurrentScope()`   | 1.0.0 | Return the current scope for the active span / execution context.   |
| `getClient()`         | 1.0.0 | Return the client by walking the scope chain. Never returns `null`. |

### [Scope Forking](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#scope-forking)

| Function                       | Since | Description                                                                                    |
| ------------------------------ | ----- | ---------------------------------------------------------------------------------------------- |
| `withScope(callback)`          | 1.0.0 | Fork the current scope, invoke callback, discard the fork when done.                           |
| `withIsolationScope(callback)` | 1.0.0 | Fork both the isolation scope and current scope, invoke callback, discard the forks when done. |

### [Top-Level Data Setters](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#top-level-data-setters)

These convenience functions write to the **isolation scope**:

| Function                           | Since  | Target Scope    |
| ---------------------------------- | ------ | --------------- |
| `Sentry.setTag(key, value)`        | 1.0.0  | Isolation scope |
| `Sentry.setUser(user)`             | 1.0.0  | Isolation scope |
| `Sentry.setContext(key, value)`    | 1.0.0  | Isolation scope |
| `Sentry.addBreadcrumb(breadcrumb)` | 1.0.0  | Isolation scope |
| `Sentry.setAttributes(attributes)` | 1.10.0 | Isolation scope |

### [Capture Functions](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#capture-functions)

Capture functions operate on the **current scope**:

| Function                         | Since | Description                                |
| -------------------------------- | ----- | ------------------------------------------ |
| `Sentry.captureException(error)` | 1.0.0 | Capture an exception on the current scope. |
| `Sentry.captureMessage(message)` | 1.0.0 | Capture a message on the current scope.    |
| `Sentry.captureEvent(event)`     | 1.0.0 | Capture a raw event on the current scope.  |

### [Event ID Retrieval](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#event-id-retrieval)

| Function               | Since | Description                                                                                                                     |
| ---------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------- |
| `Sentry.lastEventId()` | 1.0.0 | Return the ID of the last event sent from the isolation scope. Useful for correlation, logging, and custom user feedback forms. |

### [Scope Data Methods](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#scope-data-methods-1)

These methods are available on any scope instance (see [Behavior: Scope Data Methods](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#scope-data-methods) for requirements):

| Method                           | Since | Description                                                                                                  |
| -------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------ |
| `set_user(user)`                 | 1.0.0 | Set user context (email, username, id, ip\_address).                                                         |
| `set_tag(key, value)`            | 1.0.0 | Set a string tag for event searching.                                                                        |
| `set_tags(tags)`                 | 1.0.0 | Convenience for multiple `set_tag` calls.                                                                    |
| `set_context(key, value)`        | 1.0.0 | Set structured context data.                                                                                 |
| `set_level(level)`               | 1.0.0 | Override event severity level.                                                                               |
| `set_fingerprint(fingerprint[])` | 1.0.0 | Set grouping fingerprint.                                                                                    |
| `add_event_processor(fn)`        | 1.0.0 | Register an event processor callback.                                                                        |
| `set_last_event_id(id)`          | 1.0.0 | Store the ID of the last event sent. Typically set on the isolation scope by the SDK after sending an event. |
| `last_event_id()`                | 1.0.0 | Return the ID of the last event sent. Typically read from the isolation scope.                               |
| `clear()`                        | 1.0.0 | Reset scope to defaults, keeping event processors.                                                           |

***

## [Examples](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#examples)

### [SDK API Usage](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#sdk-api-usage)

#### [JavaScript](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#javascript)

```javascript
// Global scope — applied to everything
Sentry.getGlobalScope().setAttributes({
  "app.version": "2.1.0",
});

// Isolation scope — per-request (automatic in server frameworks)
// Top-level APIs write here
Sentry.setTag("transaction", "GET /users");
Sentry.setUser({ id: "user-123" });

// Current scope — localized data
Sentry.withScope((scope) => {
  scope.setTag("operation", "db-query");
  scope.setContext("query", { sql: "SELECT * FROM users" });
  Sentry.captureException(error);
});
// scope is discarded here, "operation" tag does not leak
```

#### [Python](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#python)

```python
import sentry_sdk

# Global scope
sentry_sdk.get_global_scope().set_attributes({
    "app.version": "2.1.0",
})

# Isolation scope (automatic per-request in WSGI/ASGI)
sentry_sdk.set_tag("transaction", "GET /users")
sentry_sdk.set_user({"id": "user-123"})

# Current scope — localized
with sentry_sdk.new_scope() as scope:
    scope.set_tag("operation", "db-query")
    sentry_sdk.capture_exception(error)
```

### [Scope Forking Behavior](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#scope-forking-behavior)

The diagram shows how `withScope()` and `withIsolationScope()` fork scopes and how data flows between them.

***

## [Changelog](https://develop.sentry.dev/sdk/foundations/state-management/scopes.md#changelog)

| Version  | Date                     | Summary                                                                                    |
| -------- | ------------------------ | ------------------------------------------------------------------------------------------ |
| `1.15.0` | 2026-02-03T00:00:00.000Z | Clarified array and unit support for scope attributes                                      |
| `1.14.0` | 2025-12-19T00:00:00.000Z | Clarified current scope attribute application wording                                      |
| `1.13.0` | 2025-11-25T00:00:00.000Z | Extracted dedicated attributes spec, scopes reference it                                   |
| `1.12.0` | 2025-11-18T00:00:00.000Z | Removed explicit type from user-facing attribute API, SDKs SHOULD infer type               |
| `1.11.0` | 2025-11-11T00:00:00.000Z | Added scope attribute precedence rules, removeAttribute method, telemetry-level precedence |
| `1.10.0` | 2025-11-03T00:00:00.000Z | Added global attributes on scopes (setAttributes, setAttribute)                            |
| `1.9.0`  | 2025-10-17T00:00:00.000Z | Added scope inheritance/merging diagram                                                    |
| `1.8.0`  | 2024-11-26T00:00:00.000Z | Formalized as normative spec with RFC 2119 keywords                                        |
| `1.7.0`  | 2024-11-05T00:00:00.000Z | Added scope forking diagrams                                                               |
| `1.6.0`  | 2024-08-05T00:00:00.000Z | Python scope API examples                                                                  |
| `1.5.0`  | 2024-04-03T00:00:00.000Z | Old-to-new API mapping table                                                               |
| `1.4.0`  | 2024-03-19T00:00:00.000Z | POTel integration — OTel Context storage, hooks, span-to-scope mapping                     |
| `1.3.0`  | 2024-03-11T00:00:00.000Z | Clarified copy-on-write semantics                                                          |
| `1.2.0`  | 2024-03-08T00:00:00.000Z | Rationale for OTel alignment, backwards compatibility strategy                             |
| `1.1.0`  | 2024-03-01T00:00:00.000Z | Detailed scope descriptions, forking behavior                                              |
| `1.0.0`  | 2024-02-14T00:00:00.000Z | Initial spec — three-scope model (global, isolation, current), RFC 0122                    |

## Pages in this section

- [Attributes](https://develop.sentry.dev/sdk/foundations/state-management/scopes/attributes.md)
- [Breadcrumbs](https://develop.sentry.dev/sdk/foundations/state-management/scopes/breadcrumbs.md)
