---
title: "Migrate from 10.x to 11.x"
description: "Learn about migrating from Sentry JavaScript SDK 10.x to 11.x."
url: https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11/
---

# Migrate from 10.x to 11.x | Sentry for Next.js

Version 11 of the Sentry JavaScript SDK focuses on better OpenTelemetry interoperability, more flexible instrumentation, and better out-of-the-box defaults. The biggest changes are:

* **OpenTelemetry interoperability:** The SDK no longer takes over your OpenTelemetry setup.
* **Instrumentation:** The instrumentations of the SDK now runs through [diagnostics channels](https://nodejs.org/api/diagnostics_channel.html#diagnostics-channel), which unlocks tracing on platforms like Vercel, Netlify and Cloudflare, but also runtimes like Bun and Deno.
* **Span streaming:** [Stream mode](https://docs.sentry.io/platforms/javascript/tracing/streamed-spans.md) is the new default, so spans no longer hit the size and volume limits of transactions.
* **Data collection:** `sendDefaultPii` is replaced by `dataCollection`, which controls each category of data separately and collects more by default ([Read more](https://blog.sentry.io/datacollection-control-panel/) about `dataCollection`)
* **Version support:** Node.js 20.19.0 is the new minimum. We also raised the minimum TypeScript version and the minimum versions of several frameworks.

We recommend that you upgrade to the most recent 10.x release first, because most of what v11 removes is already deprecated there.

Version 11 of the SDK requires Sentry self-hosted 26.4.2 or higher. Lower versions may continue to work, but are not supported. We recommend that you update self-hosted Sentry to the latest version.

## [Version Support Changes](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#version-support-changes)

Version 11 has new compatibility ranges for runtimes, frameworks, and libraries.

### [Node.js](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#nodejs)

Node.js 18 is no longer supported. The minimum is **20.19.0**, and Node.js 22 needs **22.12** or higher while Node.js 23 needs **23.2** or higher (`>=20.19.0 <22.0.0 || >=22.12.0 <23.0.0 || >=23.2.0`).

Move to a supported version everywhere the SDK is installed or your app is built: Dockerfiles, CI images, `engines`, `.nvmrc`, and serverless runtime settings.

### [Browsers](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#browsers)

Safari 14 is no longer supported. It lacks `performance.timeOrigin` and full `visibilitychange` support, which the web vitals instrumentation had to work around. The SDK still requires ES2020, so the rest of the matrix is unchanged:

* Chrome 80
* Edge 80
* Safari 15, iOS Safari 15
* Firefox 74
* Opera 67
* Samsung Internet 13.0

Browser projects also need Node.js 20.19.0 or higher if their build steps run on Node.js.

### [TypeScript](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#typescript)

The minimum required TypeScript version is **5.0.4**. The SDK no longer ships down-leveled types. Older TypeScript versions *may* continue to work, but no guarantees apply.

### [Next.js](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#nextjs)

Next.js 13 is no longer supported. The minimum version is **14**.

### [Libraries](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#libraries)

Fastify 3.0 through 3.20 are no longer supported. The minimum version is **3.21**.

## [Data Collection](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#data-collection)

`sendDefaultPii` is replaced by `dataCollection`, which controls each category of collected data separately. Our blog post [The Data Collection Control Panel](https://blog.sentry.io/datacollection-control-panel/) covers the thinking behind it.

This is a behavior change, not a rename. In v10, an unset `sendDefaultPii` was restrictive. In v11, an unset `dataCollection` collects everything by default.

| Category              | v10 default (`sendDefaultPii` off) | v11 default          |
| --------------------- | ---------------------------------- | -------------------- |
| `userInfo`            | `false`                            | `true`               |
| `cookies`             | not collected                      | `true`               |
| `httpHeaders`         | request + response, PII scrubbed   | request + response   |
| `httpBodies`          | not collected (size only)          | all request/response |
| `urlQueryParams`      | `true`                             | `true`               |
| `genAI`               | inputs + outputs not collected     | inputs + outputs     |
| `databaseQueryData`   | `false`                            | `true`               |
| `stackFrameVariables` | `true`                             | `true`               |
| `frameContextLines`   | `7`                                | `5`                  |

If you set `sendDefaultPii: true`, remove it. The v11 default matches that behavior.

If you relied on the v10 default, set the baseline explicitly.

Sentry scrubs values whose key looks sensitive (e.g. `auth`, `token`, `secret`, `password`). The match runs on the key name, so treat it as best effort: a credential in a field that isn't named like one still reaches Sentry. Review your [data scrubbing](https://docs.sentry.io/platforms/javascript/guides/nextjs/data-management/sensitive-data.md) config for the categories v11 collects now, especially request and response bodies.

Keeping the v10 collection defaults

```js
Sentry.init({
  dataCollection: {
    userInfo: false,
    cookies: false,
    httpHeaders: {
      request: { deny: ["forwarded", "-ip", "remote-", "via", "-user"] },
      response: { deny: ["forwarded", "-ip", "remote-", "via", "-user"] },
    },
    httpBodies: [],
    urlQueryParams: { deny: ["forwarded", "-ip", "remote-", "via", "-user"] },
    genAI: { inputs: false, outputs: false },
    databaseQueryData: false,
    graphQL: { document: false, variables: false },
  },
});
```

The `cookies`, `urlQueryParams`, and `httpHeaders` fields also accept `true`, `false`, `{ allow: string[] }`, or `{ deny: string[] }`.

Overriding a category for a single integration

The `include` options of `requestDataIntegration` stay an integration-level override. An explicit `false` keeps a category off the event, and an explicit `true` attaches it even when `dataCollection` has that category disabled. Any `allow` or `deny` filtering still applies, and a category that only `include` enables is filtered with the default denylist for sensitive values.

User IP address inference is now controlled by `dataCollection.userInfo`. To keep it for the data this integration collects, set `requestDataIntegration({ include: { ip: true } })`.

See the [`dataCollection` option](https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options.md#dataCollection) for the full list of categories.

## [Tracing](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#tracing)

Tracing moves to span streaming in v11. Instead of holding a trace in memory and sending one transaction when the root span ends, the SDK sends spans in small batches as they finish. Transactions no longer exist: what used to be one transaction event is a service span with its child spans.

That changes how spans are sent, named, and filtered. Most of it isn't caught by TypeScript, so check your filters, dashboards, and alerts.

Staying on transaction mode

Set `traceLifecycle: 'static'` to keep the previous model. In Node.js, Bun, Vercel Edge, and Cloudflare you can set the `SENTRY_TRACE_LIFECYCLE=static` environment variable instead.

```js
Sentry.init({
  traceLifecycle: "static",

  // `beforeSendSpan` must be wrapped with `Sentry.withStaticSpan`
  beforeSendSpan: Sentry.withStaticSpan((span) => {
    span.description = scrub(span.description);
    return span;
  }),
});
```

In transaction mode, `spanToStaticSpanJSON` returns the static `SpanJSON` format.

Transaction mode only exists for backwards compatibility and will be removed in a future major version. Treat it as a temporary measure.

### [Span Streaming Is the Default](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#span-streaming-is-the-default)

Spans are no longer capped at 1000 per transaction, and individual payload limits are higher. See [stream mode](https://docs.sentry.io/platforms/javascript/guides/nextjs/tracing/streamed-spans.md) for how it works.

Because no transaction events are produced, `beforeSendTransaction` and `ignoreTransactions` no longer do anything, and `beforeSendSpan` [receives a different payload](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#the-beforesendspan-payload-changed).

#### [Scope `tags` and `extra` Aren't Applied to Spans](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#scope-tags-and-extra-arent-applied-to-spans)

Streamed spans only carry attributes, so scope `tags` and `extra` no longer reach any span, including the service span that replaced the transaction. They still apply to errors, so you don't have to remove them. In transaction mode (`traceLifecycle: 'static'`) they keep landing on the transaction as before.

Set attributes for everything that should be searchable on spans, which also applies them to logs and metrics:

```js
// Before: applied to the transaction
Sentry.setTag("order_id", order.id);
Sentry.setTags({ user_tier: user.tier });

// After: applied to spans, logs, and metrics
Sentry.setAttribute("order_id", order.id);
Sentry.setAttributes({ user_tier: user.tier });
```

Attributes accept strings, numbers, booleans, and arrays of those, so numbers and booleans no longer have to be stringified. Like tags, they can be set on a specific scope:

```js
// Applied to all spans, logs, and metrics of the application
Sentry.getGlobalScope().setAttributes({ "app.version": "2.1.0" });

// Applied to a single operation
Sentry.withScope((scope) => {
  scope.setAttribute("checkout.step", "payment");
});
```

#### [The `beforeSendSpan` Payload Changed](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#the-beforesendspan-payload-changed)

The callback now runs as each span finishes and receives a `StreamedSpanJSON` object. Its fields were renamed:

| Before (`SpanJSON`) | After (`StreamedSpanJSON`)     |
| ------------------- | ------------------------------ |
| `description`       | `name`                         |
| `data`              | `attributes`                   |
| `op`                | `attributes['sentry.op']`      |
| `timestamp`         | `end_timestamp`                |
| `status` (`string`) | `status` (`'ok'` or `'error'`) |

```js
// Before
Sentry.init({
  beforeSendSpan: (span) => {
    if (span.op === "db.query") {
      span.description = scrub(span.description);
      span.data["db.statement"] = scrub(span.data["db.statement"]);
    }
    return span;
  },
});

// After
Sentry.init({
  beforeSendSpan: (span) => {
    if (span.attributes["sentry.op"] === "db.query") {
      span.name = scrub(span.name);
      span.attributes["db.query.text"] = scrub(
        span.attributes["db.query.text"]
      );
    }
    return span;
  },
});
```

The `status` field of that payload is always set, and holds only `'ok'` or `'error'`. The finer-grained statuses of v10 (`internal_error`, `not_found`, and the rest) all map to `'error'`, with the original value kept on the span's `sentry.status.message` attribute.

If you wrapped the callback with `withStreamedSpan()` to opt into this payload in v10, drop the wrapper. It's a no-op now that streamed payloads are the default, and it's deprecated.

A callback that doesn't match your `traceLifecycle` is never invoked: an unwrapped one is ignored in transaction mode, a wrapped one in stream mode. In transaction mode, wrap it with `Sentry.withStaticSpan()` and it keeps receiving the old `SpanJSON`. Turn on debug logging to see a warning about the mismatch.

#### [Replacing `beforeSendTransaction` and `ignoreTransactions`](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#replacing-beforesendtransaction-and-ignoretransactions)

Move data changes to `beforeSendSpan` and guard on `is_segment` to target the service span, which is what used to be the transaction. Use `ignoreSpans` to drop spans: `beforeSendSpan` can only change spans, not drop them.

```js
// Before
Sentry.init({
  ignoreTransactions: ["GET /health"],
  beforeSendTransaction: (event) => {
    event.transaction = scrubIds(event.transaction);
    return event;
  },
});

// After
Sentry.init({
  ignoreSpans: ["GET /health"],
  beforeSendSpan: (span) => {
    if (span.is_segment) {
      span.name = scrubIds(span.name);
    }
    return span;
  },
});
```

Ignoring a service span drops its child spans with it, which is the same result as dropping a transaction. Because `ignoreSpans` applies to every span, narrow the filter with the object form so that child spans with the same name are kept:

```js
Sentry.init({
  ignoreSpans: [
    { name: "GET /health", attributes: { "sentry.op": "http.server" } },
  ],
});
```

Scope `tags` and `extra` [aren't carried over to streamed spans](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#scope-tags-and-extra-arent-applied-to-spans) either.

#### [The `spanToJSON` Return Type Changed](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#the-spantojson-return-type-changed)

`Sentry.spanToJSON()` now returns a `StreamedSpanJSON` object, the same shape `beforeSendSpan` receives. The `spanToStreamedSpanJSON` helper was removed, so replace calls to it with `spanToJSON`.

### [Span Names Are Low Cardinality](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#span-names-are-low-cardinality)

In stream mode, span names follow the [Sentry span name conventions](https://getsentry.github.io/sentry-conventions/names/). URLs, route parameters, and other client-supplied values are no longer part of the name:

| Span op                  | Before                                  | After                                    |
| ------------------------ | --------------------------------------- | ---------------------------------------- |
| `http.client`            | `GET https://api.example.com/users/123` | `GET api.example.com`                    |
| `http.server`            | `GET /users/123`                        | `GET /users/:id`, or `GET`               |
| `pageload`, `navigation` | `/users/123`                            | `/users/:id`, or `Pageload`              |
| `resource.*`             | `/assets/app.js`                        | `cdn.example.com`, or `Resource`         |
| `graphql`                | `query GetUser`                         | `GraphQL query`                          |
| `queue.*`                | `my-queue process`, `poll my-topic`     | `process my-queue`, `receive my-topic`   |
| `db.query`               | `SELECT * FROM "User" WHERE "id" = $1`  | `SELECT "User"`, or `Database operation` |

`http.server` names are unchanged when the SDK resolves a route, and fall back to the method alone when it can't. `pageload` and `navigation` fall back to `Pageload` and `Navigation`.

Database spans work the same way: a statement that touches no table (`SELECT NOW()`) summarizes to the bare operation (`SELECT`), and without a statement to summarize the name falls back to the database namespace, then to `Database operation`.

Whatever the name no longer carries stays on an attribute, in both trace lifecycles: `url.full` and `url.domain`, `graphql.operation.name` and the new `graphql.processing.type`, `db.query.text`, and so on.

Full list of span name changes

| Span op                     | Before                                                                              | After                                                                                                                                     |
| --------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `pageload`, `navigation`    | The route, or the raw URL path (`/users/123`)                                       | The route, or `Pageload` and `Navigation`                                                                                                 |
| `http.server`               | The method and route, or the raw URL path (`GET /users/123`)                        | `GET /users/:id`, or the method alone (`GET`)                                                                                             |
| `http.client*`              | The method and sanitized URL (`GET https://api.example.com/users/123`)              | The method and domain (`GET api.example.com`), or the method alone                                                                        |
| `router`                    | Framework-specific, sometimes the raw URL (`SvelteKit Route Change`)                | The span's `http.route`, or `Router`                                                                                                      |
| `handler`                   | Framework-specific, often with the request method (`GET /users/:id`, `getUser`)     | The span's `http.route`, or `Request handler`                                                                                             |
| `graphql`                   | The phase and operation name (`query GetUser`, `graphql.resolve user.0.name`)       | The operation or processing type (`GraphQL query`, `GraphQL parse`)                                                                       |
| `gen_ai` inference ops      | `{operation} {model}`, or `{operation} unknown` (`chat unknown`)                    | `{operation} {model}`, or `{operation}` (`chat`)                                                                                          |
| `gen_ai.invoke_agent`       | The LangChain chain name, prefixed with `chain` (`chain format_prompt`)             | `{operation} {name}` from `gen_ai.agent.name`, `gen_ai.pipeline.name`, or `gen_ai.function_id`, or `{operation}`                          |
| `resource.*`                | The resource URL (`/assets/app.js`)                                                 | The resource domain (`cdn.example.com`), or `Resource`                                                                                    |
| `mcp.server`                | The method and its target (`resources/read file:///docs/api.md`)                    | The method alone for resource methods (`resources/read`). Tool and prompt names are unchanged                                             |
| `mcp.notification.*`        | The notification method name                                                        | The notification method name, or `MCP notification`                                                                                       |
| `queue.publish`             | Integration-specific (`publish my-exchange`, `send my-topic`)                       | The operation type and destination (`send my-exchange`), or the operation type alone                                                      |
| `queue.process`             | Integration-specific (`my-queue process`)                                           | The operation type and destination (`process my-queue`), or the operation type alone                                                      |
| `queue.receive`             | The kafkajs operation name (`poll my-topic`)                                        | The operation type and destination (`receive my-topic`)                                                                                   |
| `cache.*`                   | The cache keys (`user:123`), or for dataloader the operation and loader name        | The cache operation (`cache.get`, `cache.put`, `cache.remove`)                                                                            |
| `db`, `db.query` (SQL)      | The statement the driver ran (`SELECT * FROM "User" WHERE "id" = $1`)               | A summary of it (`SELECT "User"`), or, without a statement, the operation and table, the namespace, or the database system (`postgresql`) |
| `db` (MongoDB)              | The serialized command, or `mongodb.<operation>` (`mongodb.find`)                   | The operation and collection (`find users`), the database namespace, or `mongodb`                                                         |
| `db` (Mongoose)             | `mongoose.<Model>.<operation>` (`mongoose.BlogPost.findOne`)                        | The operation and collection (`findOne blogposts`), the database namespace, or `mongodb`                                                  |
| `db` (Supabase)             | The query builder call and table (`select(...) from(users)`), or `auth <method>`    | The operation and table (`select users`), or the dotted auth method (`auth.signInWithPassword`)                                           |
| `db.query` (Redis, ioredis) | The serialized command with redacted arguments (`set test-key [1 other arguments]`) | The operation and connection (`SET localhost:6379`), the operation and redis function for `FCALL`, or `redis`                             |

A few consequences worth checking:

* `useOperationNameForRootSpan` no longer renames the enclosing root span. The operations stay on that span's `sentry.graphql.operation` attribute.
* `graphqlClientIntegration` no longer appends the operation to the outgoing request span name. It's on `graphql.operation.name` and `graphql.operation.type` instead.
* Only the Express, Koa, and Hapi integrations resolve a route for `router` spans. Angular, Ember, and SvelteKit router spans are named `Router`.
* Express, Fastify, Hapi, and Elysia resolve a route for `handler` spans. NestJS has none at span start, so its handler spans are named `Request handler`. The handler function name moved to `nestjs.callback` for NestJS and `code.function.name` for Elysia.
* Cache keys are no longer part of a cache span name. They stay on `cache.key`, and every cache span carries the new `cache.operation` attribute (`get`, `put`, `remove`). This affects the Redis and ioredis cache spans, the Nuxt and Nitro storage spans, and the dataloader spans, which report the loader name on `db.collection.name` now. A Redis command matching `cachePrefixes` starts as a cache span now instead of turning into one at response time, and `maxCacheKeyLength` has no effect in stream mode.
* The `pg`, `postgres.js`, `mysql`, `mysql2`, `knex`, `tedious`, Prisma, Nitro `db0`, and Cloudflare D1 instrumentations name their query spans the same way now. A statement that touches no table (`SELECT NOW()`) summarizes to the bare operation. Connect and pool spans (`pg.connect`, `mysql2.connect`, `redis-connect`, `generic-pool.acquire`) keep their names.
* Supabase query spans drop the builder call from the name, and the modifiers stay on `db.query`. Auth spans are named after the method they call (`auth.admin.createUser`).
* AWS SQS `SendMessage`, `SendMessageBatch`, and `ReceiveMessage`, and SNS `Publish`, are messaging spans now instead of `rpc` spans. Every other command on those clients stays `rpc`.
* Child spans carry their service span's name in `sentry.segment.name`, so that changes with it.

`ignoreSpans` and `tracesSampler` both run when a span **starts**, so a span may not have its final name yet. Match on attributes instead:

```js
Sentry.init({
  // Before
  ignoreSpans: ["/health", "SELECT * FROM health_check"],
  tracesSampler: ({ name, inheritOrSampleWith }) =>
    inheritOrSampleWith(name === "GET /health" ? 0 : 1),

  // After
  ignoreSpans: [
    { attributes: { "sentry.op": "pageload", "url.path": "/health" } },
    { attributes: { "db.query.text": "SELECT * FROM health_check" } },
  ],
  tracesSampler: ({ attributes, inheritOrSampleWith }) =>
    inheritOrSampleWith(attributes["url.path"] === "/health" ? 0 : 1),
});
```

### [Span Operations Were Consolidated](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#span-operations-were-consolidated)

Span ops are now a smaller, framework-neutral set. The detail that used to live in the op, such as the framework, library, method name, or lifecycle phase, is preserved in attributes like `code.function.name`, `sentry.origin`, `db.system.name`, `db.operation.name`, and `faas.trigger`.

Update anything that filters, groups, or alerts on span ops: dashboards, dynamic sampling rules, `ignoreSpans`, and `beforeSendSpan`. TypeScript doesn't catch these.

Full list of span op changes

**Backend HTTP, handlers, middleware, and routers**

| Area                                                              | Before                                                                                                                                                                                                                     | After         |
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| Request handlers (Express, Koa, Connect, Fastify, Elysia, NestJS) | `request_handler.<library>`, `handler.nestjs`                                                                                                                                                                              | `handler`     |
| Hono `app.request()` in-process dispatch                          | `hono.request`                                                                                                                                                                                                             | `http.server` |
| Web-server middleware                                             | `middleware.express`, `middleware.koa`, `middleware.hono`, `middleware.elysia`, `middleware.nestjs`, `middleware.nuxt`, `middleware.nitro`, `middleware.tanstackstart`, `hook.fastify`, `http.server.middleware` (Next.js) | `middleware`  |
| Backend router layers                                             | `router.express`, `router.koa`, `router.hapi`                                                                                                                                                                              | `router`      |
| Hapi server extensions                                            | `server.ext.hapi`                                                                                                                                                                                                          | `middleware`  |
| NestJS setup and lifecycle handlers                               | `app_creation.nestjs`, `request_context.nestjs`, `event.nestjs`                                                                                                                                                            | `function`    |

**Framework functions**

| Area                                                                                                         | Before                                                                                                                                                                                              | After      |
| ------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| Loaders, actions, and server functions (Next.js, Remix, React Router, SvelteKit, SolidStart, TanStack Start) | `function.nextjs`, `function.sveltekit.load`, `function.react_router.loader`, `function.remix.document_request`, `loader.remix`, `action.remix`, `function.server_action`, `function.tanstackstart` | `function` |

**Frontend and UI**

| Area                                        | Before                                                                                                                               | After                                                    |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- |
| Frontend routing                            | `ui.angular.routing`, `ui.sveltekit.routing`, `ui.ember.transition`                                                                  | `router`                                                 |
| React, Vue, and Svelte component lifecycles | `ui.react.mount`/`render`/`update`, `ui.svelte.init`/`update`, Vue `render`/`update`/`mount`/`create`/`activate`/`unmount`/`destroy` | `ui.mount`, `ui.render`, `ui.update`, `ui.unmount`       |
| Angular tracing decorators                  | `ui.angular.init`, `ui.angular.<method>`                                                                                             | `ui.mount`, `function`                                   |
| Ember route hooks, runloop, and components  | `ui.ember.route.<hook>`, `ui.ember.runloop.<queue>`, `ui.ember.component.render`/`definition`/`init`                                 | `function`, `ui.task`, `ui.render`/`function`/`ui.mount` |
| Browser paint entries                       | `paint`                                                                                                                              | `browser.paint`                                          |

**Databases, cache, and messaging**

| Area                                     | Before                                                                                                                                           | After                                             |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------- |
| Redis commands and connect               | `db.redis`, `db.redis.connect`                                                                                                                   | `db.query`, `db`                                  |
| Nuxt and Nitro storage (unstorage)       | `cache.has_item`, `cache.get_item`, `cache.get_items`, `cache.get_keys`, `cache.set_item`, `cache.set_items`, `cache.remove_item`, `cache.clear` | `cache.get`, `cache.put`, `cache.remove`          |
| Kafka, AMQP, and OTel-inferred messaging | `message`, `message.produce`, `message.consume`                                                                                                  | `queue.publish`, `queue.receive`, `queue.process` |
| AWS SQS and SNS messaging commands       | `rpc`                                                                                                                                            | `queue.publish`, `queue.receive`                  |

**RPC and Gen AI**

| Area                                                       | Before                                      | After                                    |
| ---------------------------------------------------------- | ------------------------------------------- | ---------------------------------------- |
| tRPC                                                       | `rpc.server`                                | `rpc`                                    |
| GCP gRPC calls                                             | `grpc.<service>`                            | `grpc`                                   |
| AWS Bedrock inference                                      | `rpc`                                       | `gen_ai.chat`, `gen_ai.generate_content` |
| Gen AI fallbacks and model metadata (Vercel AI, LangGraph) | `gen_ai.unknown`, `ai.run`, `gen_ai.models` | `function`                               |

**FaaS, serverless, and HTTP clients**

| Area                                           | Before                                                                | After                                      |
| ---------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------ |
| AWS Lambda functions                           | `function.aws.lambda`                                                 | `function.aws`                             |
| GCP functions                                  | `function.gcp.http`, `function.gcp.event`, `function.gcp.cloud_event` | `function.gcp`                             |
| Firebase functions                             | `http.request`                                                        | `function.gcp`                             |
| Cloudflare cron, email, and workflow steps     | `faas.cron`, `faas.email`, `function.step.do`                         | `function`                                 |
| OTel-inferred FaaS spans (from `faas.trigger`) | arbitrary trigger strings used verbatim                               | `http.server`, `queue.process`, `function` |
| GCP HTTP client                                | `http.client.<service>`                                               | `http.client`                              |
| Prefetch HTTP requests                         | `http.client.prefetch`, `http.server.prefetch`                        | `http.client`, `http.server`               |

**Casing normalized to snake\_case**

| Before                          | After                              |
| ------------------------------- | ---------------------------------- |
| `ui.long-task`                  | `ui.long_task`                     |
| `ui.long-animation-frame`       | `ui.long_animation_frame`          |
| `browser.unloadEvent`           | `browser.unload_event`             |
| `browser.domContentLoadedEvent` | `browser.dom_content_loaded_event` |
| `browser.loadEvent`             | `browser.load_event`               |
| `browser.TLS/SSL`               | `browser.tls_ssl`                  |
| `browser.DNS`                   | `browser.dns`                      |

### [Span Attributes Follow the Current Conventions](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#span-attributes-follow-the-current-conventions)

Legacy span attributes were replaced by their semantic-convention equivalents. Update any custom instrumentation, `beforeSendSpan` logic, dashboards, or alerts that reference them.

HTTP and network attributes

| v10 attribute                          | v11 attribute                     |
| -------------------------------------- | --------------------------------- |
| `http.host`                            | `server.address`                  |
| `http.flavor`                          | `network.protocol.version`        |
| `http.client_ip`                       | `client.address`                  |
| `http.method`                          | `http.request.method`             |
| `http.status_code`                     | `http.response.status_code`       |
| `http.status_text`                     | `http.response.status_text`       |
| `http.scheme`                          | `url.scheme`                      |
| `http.user_agent`                      | `user_agent.original`             |
| `http.request_content_length`          | `http.request.body.size`          |
| `http.response_content_length`         | `http.response.body.size`         |
| `http.decoded_response_content_length` | `http.response.body.decoded_size` |
| `http.response_transfer_size`          | `http.response.size`              |
| `http.target`                          | `url.path` + `url.query`          |
| `http.query`                           | `url.query`                       |
| `http.fragment`                        | `url.fragment`                    |
| `url.same_origin`                      | `http.request.same_origin`        |
| `net.host.name`, `net.peer.name`       | `server.address`                  |
| `net.host.ip`                          | `network.local.address`           |
| `net.host.port`                        | `network.local.port`              |
| `net.peer.ip`                          | `network.peer.address`            |
| `net.peer.port`                        | `network.peer.port`               |
| `net.transport`                        | `network.transport`               |

`SanitizedRequestData`, the shape used for `http` breadcrumb data and `http.client` span data, uses `http.request.method` as the key for the request method now.

Transport values change from `ip_tcp` and `ip_udp` to `tcp` and `udp`. HTTP instrumentation reports the protocol as `network.protocol.name` and its version as `network.protocol.version`.

On server-side HTTP spans, the `content-length` header is always reported as `http.request.body.size` and `http.response.body.size`, instead of switching to `http.request_body_size_uncompressed` when no encoding was present.

Database and messaging attributes

| v10 attribute           | v11 attribute        |
| ----------------------- | -------------------- |
| `db.system`             | `db.system.name`     |
| `db.name`               | `db.namespace`       |
| `db.operation`          | `db.operation.name`  |
| `db.statement`          | `db.query.text`      |
| `db.mongodb.collection` | `db.collection.name` |
| `net.peer.name`         | `server.address`     |
| `net.peer.port`         | `server.port`        |

SQL query spans carry a new `db.query.summary` attribute, holding the summary of the sanitized statement (`SELECT "User"`). It's set in both trace lifecycles.

Mongoose spans report `db.system.name: 'mongodb'` instead of `'mongoose'`, because Mongoose is an ODM, not a database system.

The Redis and ioredis instrumentations no longer emit `db.connection_string`. The connection is described by `server.address` and `server.port` instead.

The AMQP instrumentation reports `messaging.destination.name`, `messaging.rabbitmq.destination.routing_key`, `messaging.message.id`, `messaging.message.conversation_id`, `messaging.operation.name`, `network.protocol.name`, `network.protocol.version`, and `url.full`. It no longer emits `messaging.destination_kind`.

Gen AI attributes

| v10 attribute                              | v11 attribute                              |
| ------------------------------------------ | ------------------------------------------ |
| `gen_ai.system`                            | `gen_ai.provider.name`                     |
| `gen_ai.request.available_tools`           | `gen_ai.tool.definitions`                  |
| `gen_ai.tool.input`                        | `gen_ai.tool.call.arguments`               |
| `gen_ai.tool.output`                       | `gen_ai.tool.call.result`                  |
| `gen_ai.usage.cache_creation_input_tokens` | `gen_ai.usage.cache_creation.input_tokens` |
| `gen_ai.usage.cache_read_input_tokens`     | `gen_ai.usage.cache_read.input_tokens`     |
| `gen_ai.usage.input_tokens.cached`         | `gen_ai.usage.cache_read.input_tokens`     |
| `gen_ai.usage.input_tokens.cache_write`    | `gen_ai.usage.cache_creation.input_tokens` |
| `gen_ai.usage.output_tokens.reasoning`     | `gen_ai.usage.reasoning.output_tokens`     |
| `ai.pipeline.name`                         | `gen_ai.pipeline.name`                     |
| `ai.streaming`                             | `gen_ai.response.streaming`                |
| `langchain.chain.name`                     | `gen_ai.pipeline.name`                     |

`gen_ai.tool.type` is no longer set on tool spans. `gen_ai.pipeline.name` is omitted for unnamed chains, instead of holding `unknown_chain`.

The Anthropic integration no longer sets `gen_ai.prompt`. The legacy Completions API prompt is reported as a user message on `gen_ai.input.messages`, like every other request shape.

Other attributes and constants

* On `ui.long_animation_frame` spans, `code.filepath` and `code.function` became `code.file.path` and `code.function.name`.
* On `file` spans, `fs_error` became `error.type` and holds the syscall error code (`ENOENT`) instead of the full message.
* The TanStack Router integration reports path parameters as `url.path.parameter.<key>` instead of `url.path.params.<key>`.
* The Cloudflare-specific `sentry.cloudflare_tracer` attribute is no longer set.
* Import attribute constants from `@sentry/core` directly. The `semanticAttributes` re-export was removed.
* `SEMANTIC_ATTRIBUTE_SENTRY_SOURCE` (`sentry.source`) was replaced by `SENTRY_SEGMENT_NAME_SOURCE` (`sentry.segment.name.source`), which is only set on the root span.

## [OpenTelemetry](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#opentelemetry)

Sentry and OpenTelemetry are separate pipelines now, and you choose how they connect.

### [The SDK No Longer Sets Up OpenTelemetry](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#the-sdk-no-longer-sets-up-opentelemetry)

By default, the SDK no longer registers an OpenTelemetry tracer provider. It owns the full span lifecycle, produces native Sentry spans, and ignores spans created through `@opentelemetry/api`. You can now run your own OpenTelemetry setup next to Sentry without Sentry spans leaking into your pipeline, and without using Sentry components for export, context management, and trace propagation.

If you only use the Sentry SDK, tracing works as before and there's nothing to do.

`skipOpenTelemetrySetup` is replaced by `enableOpenTelemetrySetup`. Turn it on and the SDK registers a custom OpenTelemetry tracer provider, context manager, and propagator, which turns spans from `@opentelemetry/api` into Sentry spans. It's off by default, except on `@sentry/nextjs` and `@sentry/sveltekit`, which need it to capture the spans those frameworks emit.

Sending spans from @opentelemetry/api to Sentry

Turn this on when a library you depend on, or your own code, creates spans through `@opentelemetry/api` and you want them in Sentry:

```js
import { trace } from "@opentelemetry/api";

// Somewhere in your app, or inside a dependency
const tracer = trace.getTracer("my-library");

tracer.startActiveSpan("work", (span) => {
  doWork();
  span.end();
});
```

With `enableOpenTelemetrySetup: true`, the span is captured, without it, the span is ignored. `enableOpenTelemetrySetup` isn't meant for setups that already run their own OpenTelemetry pipeline, which is the next section.

```js
Sentry.init({
  dsn: "https://<key>@o<orgId>.ingest.sentry.io/<projectId>",
  tracesSampleRate: 1.0,
  enableOpenTelemetrySetup: true,
});
```

This is not a general OpenTelemetry pipeline: no exporter, no OTLP output. Sentry also refuses to register its provider if you already registered one of your own, and the `@sentry/cloudflare/request` entry point doesn't support the option at all.

Running your own OpenTelemetry setup

Keep `enableOpenTelemetrySetup` off, turn Sentry tracing off, and add `openTelemetryIntegration()` to connect Sentry errors, logs, metrics, and crons to your OpenTelemetry traces. `getOtlpTracesEndpoint()` turns your DSN into the URL and auth headers of Sentry's OTLP endpoint.

```js
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import * as Sentry from "@sentry/node";

const provider = new NodeTracerProvider({
  spanProcessors: [
    new BatchSpanProcessor(
      new OTLPTraceExporter(Sentry.getOtlpTracesEndpoint("https://<key>@o<orgId>.ingest.sentry.io/<projectId>"))
    ),
  ],
});

provider.register();

Sentry.init({
  dsn: "https://<key>@o<orgId>.ingest.sentry.io/<projectId>",
  // No `tracesSampleRate`: OpenTelemetry owns spans, Sentry owns errors and logs
  integrations: [Sentry.openTelemetryIntegration()],
});
```

On `@sentry/nextjs` and `@sentry/sveltekit`, set `enableOpenTelemetrySetup: false` explicitly.

The two pipelines stay separate: Sentry sends no spans, and no Sentry span reaches your OpenTelemetry pipeline.

This setup only works with Sentry tracing off, so leave `tracesSampleRate` unset. Sentry instruments many of the libraries OpenTelemetry does, so leaving tracing on gives you two spans for every operation, in two pipelines that never join up. With tracing off, Sentry's instrumentation stays installed and keeps isolating requests, but emits no spans. Note that this changed from v10, where `skipOpenTelemetrySetup: true` also turned off Sentry's HTTP and fetch spans.

If you used the v10 integration from `@sentry/node-core/light/otlp`: it moved to the main export of every server SDK, it no longer sets up an exporter (the `setupOtlpTracesExporter` and `collectorUrl` options were removed), and it was renamed to `openTelemetryIntegration()`.

`SentryContextManager`, `SentrySampler`, and `SentrySpanProcessor` were removed, so there is no longer a way to route spans from your own provider into Sentry as Sentry spans. Export them over OTLP instead.

## [Instrumentation](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#instrumentation)

How the SDK hooks into your app changed, along with the way some frameworks are set up.

### [Channel-Based Instrumentation Is the Default](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#channel-based-instrumentation-is-the-default)

Instrumentation now runs through diagnostics channels (`orchestrion`) instead of `import-in-the-middle`. It allows instrumentation at run time and build time, which enables tracing on deployment targets like Vercel and Netlify, and on non-Node.js runtimes like Cloudflare, Bun, and Deno. Most setups need no changes.

The `vercelAIIntegration` is the exception. It no longer works on Vercel Edge, which doesn't support diagnostics channels, and it no longer enhances the native OpenTelemetry spans the `ai` package emits. Those spans are captured as they are, so some agent monitoring capabilities are lost.

### [Initializing With `--require` Is No Longer Supported](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#initializing-with---require-is-no-longer-supported)

Node.js re-runs `--require` preloads on the internal module loader thread that the SDK spawns when it installs its instrumentation hooks, so `Sentry.init()` ran a second time on a thread that never executes your code. The SDK now skips initialization on that thread and warns when it was loaded through `--require`.

Use [`--import`](https://nodejs.org/api/cli.html#--importmodule) instead. It works for both ESM and CommonJS apps.

```bash
# Before
node --require ./instrument.js app.js

# After
node --import ./instrument.js app.js
```

### [Framework Errors Are Captured Automatically](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#framework-errors-are-captured-automatically)

Express, Fastify, Koa, and Hapi errors are captured by their integrations now, so the error handler setup calls are no longer needed. They are deprecated and will be removed in the next major version.

* `setupExpressErrorHandler(app)`
* `setupFastifyErrorHandler(app)`
* `setupKoaErrorHandler(app)`
* `setupHapiErrorHandler(server)`

Because the integrations own error capture, `shouldHandleError` moved to `expressIntegration()` and `fastifyIntegration()`:

```js
// Before
Sentry.init({ integrations: [Sentry.expressIntegration()] });

Sentry.setupExpressErrorHandler(app, {
  shouldHandleError(error) {
    return Number(error.statusCode ?? 500) >= 400;
  },
});

// After
Sentry.init({
  integrations: [
    Sentry.expressIntegration({
      shouldHandleError(error) {
        return Number(error.statusCode ?? 500) >= 400;
      },
    }),
  ],
});
```

By default, Express captures 5xx errors and errors without a resolvable status, but not 3xx and 4xx errors. To capture errors yourself instead, set `expressIntegration({ shouldHandleError: false })` and call `Sentry.captureException` in your own error-handling middleware.

`setupExpressErrorHandler` and `expressErrorHandler` moved from `@sentry/core` to `@sentry/server-utils`, along with `patchExpressModule`, which is deprecated for the same reason. Import them from your platform SDK, such as `@sentry/node`, as before.

### [Other `httpIntegration` Changes](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#other-httpintegration-changes)

The deprecated `instrumentation.requestHook`, `instrumentation.responseHook`, and `instrumentation.applyCustomAttributesOnSpan` hooks were removed, and several options were renamed. This only matters if you configured `httpIntegration` or `httpServerSpansIntegration` yourself.

httpIntegration hook and option changes

In v10 the `instrumentation` hooks ran for incoming and outgoing spans, so which replacement you need depends on the spans your hook changed:

```js
// Before
Sentry.httpIntegration({
  instrumentation: {
    requestHook: (span, req) => {
      span.setAttribute("custom", true);
    },
  },
});

// After: incoming (server) spans
Sentry.httpIntegration({
  onSpanCreated: (span, req, res) => {
    span.setAttribute("custom", true);
  },
});

// After: outgoing (client) spans
Sentry.httpIntegration({
  outgoingRequestHook: (span, req) => {
    span.setAttribute("custom", true);
  },
});
```

The other `httpIntegration` options were renamed to match the other server SDKs:

| Removed option                           | Replacement          |
| ---------------------------------------- | -------------------- |
| `trackIncomingRequestsAsSessions`        | `sessions`           |
| `maxIncomingRequestBodySize`             | `maxRequestBodySize` |
| `ignoreIncomingRequestBody`              | `ignoreRequestBody`  |
| `dropSpansForIncomingRequestStatusCodes` | `ignoreStatusCodes`  |
| `incomingRequestSpanHook`                | `onSpanCreated`      |

`ignoreStatusCodes` is deprecated on `httpIntegration` and `httpServerSpansIntegration`, and will be removed in v12 without a replacement. It filters finished transaction events, so it only has an effect with `traceLifecycle: 'static'`. In stream mode, child spans are sent before the response status is known. To keep requests out of Sentry, decide before they're instrumented, with `tracesSampler`, `ignoreSpans` or `ignoreIncomingRequests`:

```js
Sentry.init({
  integrations: [
    Sentry.httpIntegration({
      ignoreIncomingRequests: (urlPath) => urlPath.startsWith("/admin"),
    }),
  ],
});
```

### [Unhandled Rejections Print Less in `strict` Mode](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#unhandled-rejections-print-less-in-strict-mode)

In `strict` mode, the integration printed a warning in front of every unhandled rejection. It's now printed only when the rejection reason has no stack trace, which matches Node.js. Update log processing or tests that match on that text. The process still exits with code `1`, and the reason is still written to `stderr`.

## [Logs and Metrics](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#logs-and-metrics)

The `enableLogs` and `enableMetrics` options were removed, including their `_experiments` variants. Logs and metrics are captured whenever you use their APIs (`Sentry.logger.*`, `Sentry.metrics.*`) or add a logging integration such as `consoleLoggingIntegration()`. The `_experiments.beforeSendMetric` callback moved to the top-level `beforeSendMetric` option.

```js
// Before
Sentry.init({
  enableLogs: true,
  _experiments: {
    enableMetrics: true,
    beforeSendMetric: (metric) => metric,
  },
});

// After
Sentry.init({
  beforeSendMetric: (metric) => metric,
});
```

## [Errors and Sessions](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#errors-and-sessions)

New defaults change which events carry a stack trace and how sessions are counted.

### [Stack Traces Are Attached by Default](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#stack-traces-are-attached-by-default)

`Sentry.captureMessage()` events, and non-`Error` values passed to `Sentry.captureException()`, now attach a synthetic stack trace that points to the call site. Set `attachStacktrace: false` to restore the previous behavior.

Two consequences are worth checking after you upgrade:

* **Issue grouping:** Sentry groups events with and without stack traces differently, so you may see new issue groups.
* **Release health:** Events with a stack trace count as errors, so a `captureMessage()` call marks the current session as errored. Crash-free session rate is not affected. For purely informational output, consider [Sentry Logs](https://docs.sentry.io/product/logs.md) instead.

### [Browser Session Changes](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#browser-session-changes)

Two defaults changed for browser sessions:

* Sessions affected by an uncaught error are recorded as `unhandled` instead of `crashed`. If you track crash-free session rates or have alerts built on them, expect the rate to shift.
* The `lifecycle` mode of `browserSessionIntegration` defaults to `'page'`, so a session is created on page load and is not renewed on navigation.

```js
// Restore a new session on load and on every navigation
Sentry.init({
  integrations: [Sentry.browserSessionIntegration({ lifecycle: "route" })],
});
```

### [The `DOMException.code` Tag Was Removed](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#the-domexceptioncode-tag-was-removed)

Events created from a `DOMException` no longer carry a `DOMException.code` tag, because the `code` property is deprecated in favor of `DOMException.name`, which is already the exception type. Switch searches and alert rules that use the tag to `error.type`.

### [Feedback Rejections Are Always an `Error`](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#feedback-rejections-are-always-an-error)

`Sentry.sendFeedback()` now rejects with an `Error` in all cases. Previously it rejected with a plain string when the request timed out, was rejected with a 403, or otherwise failed to send. The message text is unchanged and is still customizable through the `errorMessages` hint, so read it from `error.message`.

### [Trace Propagation Matching Is Case-Insensitive](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#trace-propagation-matching-is-case-insensitive)

String and regular expression matching for `tracePropagationTargets` no longer depends on casing. In browsers this was especially surprising, because the URL is normalized with `new URL()` before matching, which lower-cases the origin. A target such as `'myApi.com'` could therefore never match a request to `https://myApi.com`.

```js
Sentry.init({
  // In a browser, neither of these matched `https://myApi.com` in v10. In v11 both do.
  tracePropagationTargets: ["myApi.com", /^https:\/\/myApi\.com/],
});
```

If you relied on case-sensitive matching to tell two targets apart, narrow the target with a more specific path. The `g` and `y` flags are ignored now, because they made matching stateful and a target like `/myApi\.com/g` matched only every other request.

## [Browser Integrations](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#browser-integrations)

Several `browserTracingIntegration` options moved to dedicated integrations, or were removed:

| Removed option                        | Replacement                                     |
| ------------------------------------- | ----------------------------------------------- |
| `_experiments.enableInteractions`     | `interactionsIntegration()`                     |
| `ignorePerformanceApiSpans`           | `userTimingIntegration({ ignore: [...] })`      |
| `trackFetchStreamPerformance`         | `fetchStreamPerformanceIntegration()`           |
| `_experiments.enableStandalone*Spans` | Removed, CLS and LCP are no longer configurable |

```js
// Before
Sentry.init({
  integrations: [
    Sentry.browserTracingIntegration({
      _experiments: { enableInteractions: true },
      ignorePerformanceApiSpans: ["third-party-mark"],
      trackFetchStreamPerformance: true,
    }),
  ],
});

// After
Sentry.init({
  integrations: [
    Sentry.browserTracingIntegration(),
    Sentry.interactionsIntegration(),
    Sentry.userTimingIntegration({ ignore: ["third-party-mark"] }),
    Sentry.fetchStreamPerformanceIntegration(),
  ],
});
```

`browserTracingIntegration` no longer captures `performance.mark()` and `performance.measure()` spans by default, and no longer accepts an `_experiments` object at all. The `idleTimeout`, `finalTimeout`, and `childSpanTimeout` options of interaction spans are configured on `interactionsIntegration` now, with the same defaults as before.

Web vitals also changed:

* CLS and LCP are recorded as measurements on the pageload span, or as dedicated spans in stream mode.
* INP is always sent as a web vital span, carrying its value in the `browser.web_vital.inp.value` attribute instead of as a span measurement. Update custom dashboards and alerts that read it as a measurement (built-in dashboards do not need adjustments).

## [Next.js Changes](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#nextjs-changes)

The build-time configuration moved to its own entry point, and the options deprecated in v10 were removed.

### [The `withSentryConfig` Entry Point Moved](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#the-withsentryconfig-entry-point-moved)

```js
// Before
import { withSentryConfig } from "@sentry/nextjs";

// After
import { withSentryConfig } from "@sentry/nextjs/config";
```

The `SentryBuildOptions` type moved with it, and the no-op `withSentryConfig` passthroughs of the client and edge builds were removed.

### [Removed Build Options](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#removed-build-options)

The top-level options of `withSentryConfig` that were deprecated in 10.30.0 were removed. Most of them moved under `webpack` back then:

| Removed option                          | Replacement                             |
| --------------------------------------- | --------------------------------------- |
| `autoInstrumentServerFunctions`         | `webpack.autoInstrumentServerFunctions` |
| `autoInstrumentMiddleware`              | `webpack.autoInstrumentMiddleware`      |
| `autoInstrumentAppDirectory`            | `webpack.autoInstrumentAppDirectory`    |
| `automaticVercelMonitors`               | `webpack.automaticVercelMonitors`       |
| `excludeServerRoutes`                   | `webpack.excludeServerRoutes`           |
| `disableSentryWebpackConfig`            | `webpack.disableSentryConfig`           |
| `disableLogger`                         | `webpack.treeshake.removeDebugLogging`  |
| `disableManifestInjection`              | `routeManifestInjection: false`         |
| `_experimental.turbopackApplicationKey` | `applicationKey`                        |
| `unstable_sentryWebpackPluginOptions`   | Set the plugin options as build options |

### [Unified `reactComponentAnnotation` Option](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#unified-reactcomponentannotation-option)

React component annotation is configured through a single top-level option that applies to webpack and Turbopack builds:

```js
export default withSentryConfig(nextConfig, {
  reactComponentAnnotation: {
    enabled: true,
    ignoredComponents: ["MyComponent"],
  },
});
```

The bundler-specific `webpack.reactComponentAnnotation` and `_experimental.turbopackReactComponentAnnotation` options still work, but are deprecated and will be removed in v12. If both are set, the bundler-specific one wins for that bundler.

On Turbopack, component annotation requires Next.js 16 or higher. The SDK warns at build time when annotation is enabled on an older version, where it previously did nothing.

### [The Default Environment on Vercel Lost Its Prefix](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#the-default-environment-on-vercel-lost-its-prefix)

On Vercel, the SDK defaults `environment` to `VERCEL_TARGET_ENV` (`production`, `preview`, or your own environment name) instead of `vercel-production` and `vercel-preview`. Update alert rules, dashboards, and saved searches that use the old names, or set `environment` explicitly to keep them.

### [Vercel AI Is Not Instrumented on the Edge Runtime](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#vercel-ai-is-not-instrumented-on-the-edge-runtime)

The instrumentation relies on diagnostics channels, which the Edge runtime doesn't support. The `vercelAIIntegration` export stays available as a no-op on Edge builds.

## [Build Options](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#build-options)

The bundler plugin options of the meta-framework SDKs are all first-class build options now.

### [Vercel Deploys Use the Plain Environment Name](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#vercel-deploys-use-the-plain-environment-name)

Deploys that the bundler plugins create on Vercel use `VERCEL_TARGET_ENV` as their environment now, instead of `vercel-production` and `vercel-preview`. That matches the runtime default. If your events use a different environment, set `release.deploy.env` to the same value, or set `release.deploy` to `false` to opt out.

### [The `unstable_` Bundler Plugin Options Were Removed](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#the-unstable_-bundler-plugin-options-were-removed)

The `unstable_sentry*PluginOptions` escape hatch was removed from every SDK, because the Sentry bundler plugins now live in the SDK monorepo and release in lockstep. Every supported plugin option is a first-class build option:

```js
// Before
unstable_sentryWebpackPluginOptions: {
  applicationKey: "my-app",
},

// After
applicationKey: "my-app",
```

Passing a removed option logs a build-time warning that names it, because build configs are often plain JavaScript where TypeScript can't catch it.

`moduleMetadata` and `sourcemaps.resolveSourceMap` are first-class build options now. `release.uploadLegacySourcemaps`, `_experiments`, and the whole-plugin `disable` flag have no equivalent. Use `sourcemaps.disable` instead of `disable`.

## [Removed APIs](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#removed-apis)

The changes in this section detail deprecated APIs that are now removed.

### [All SDKs](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#all-sdks)

* `@sentry/core` exports only isomorphic code now. Browser-only exports live on `@sentry/core/browser` and server-only ones on `@sentry/core/server`, which keeps server code out of browser bundles. This only affects imports straight from `@sentry/core`, since the platform SDKs re-export as before. TypeScript reports it as `has no exported member`.

```js
// Before
import { loadModule, trpcMiddleware } from "@sentry/core";
import type { BrowserClientReplayOptions } from "@sentry/core";

// After
import { loadModule, trpcMiddleware } from "@sentry/core/server";
import type { BrowserClientReplayOptions } from "@sentry/core/browser";
```

* `sendDefaultPii` was removed. Use [`dataCollection`](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#data-collection) instead.
* `Scope.clear()` was removed. To reset scope state, re-initialize the SDK or run your code in a fresh scope with `withScope` or `withIsolationScope`.
* The `disableInstrumentationWarnings` option and the `MissingInstrumentationContext` type were removed. With channel-based instrumentation, the SDK can no longer detect that a framework was imported before `Sentry.init()`.
* `createSpanEnvelope` and the `SpanEnvelope` and `SpanItem` types were removed. Standalone spans are gone: spans are sent on their transaction, or as streamed spans.
* The positional `spanOrigin` argument of `instrumentFetchRequest` was removed. Pass an options object as the last argument instead.
* The internal `addAutoIpAddressToUser` export was removed.

### [Browser](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#browser)

* The `console` option of `breadcrumbsIntegration` was removed. Use `consoleIntegration` to capture console breadcrumbs.
* AI integrations are no longer available in the browser SDK. They remain available in the server-side SDKs.

### [Node.js and Server SDKs](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#nodejs-and-server-sdks)

* The `init`, `preload`, and `loader` entry points were removed from every server SDK, along with `preloadOpenTelemetry()`. Either `--import` your own instrument file, or `--import` the SDK and call `Sentry.init()` in code:

```bash
node --import ./instrument.mjs app.js
node --import @sentry/node/import app.js
```

* The `registerEsmLoaderHooks` option was removed. The SDK no longer registers `import-in-the-middle` ESM loader hooks.
* `generateInstrumentOnce` and `SentryContextManager` are no longer exported.
* The deprecated `SentryHttpInstrumentation` and `SentryNodeFetchInstrumentation` exports were removed. Use `instrumentHttpOutgoingRequests()` and `nativeNodeFetchIntegration` instead.
* The deprecated `honoIntegration` was removed. Use the [`@sentry/hono`](https://www.npmjs.com/package/@sentry/hono) SDK instead.
* The `connect` instrumentation and the deprecated `prismaInstrumentation` option were removed. Prisma works out of the box.
* The `OpenTelemetryServerRuntimeOptions` type was removed. `enableOpenTelemetrySetup` is part of the SDK-specific options types, such as `NodeOptions`.
* OpenTelemetry resources are no longer collected. `contexts.otel.resource` was dropped from events, and the SDK no longer reads `OTEL_SERVICE_NAME` and `OTEL_RESOURCE_ATTRIBUTES`.
* `getTraceContextForScope` and `getSentryResource` were removed from `@sentry/opentelemetry`, along with the `@opentelemetry/core` peer dependency.
* (AWS Lambda) The deprecated `disableAwsContextPropagation` and `startTrace` options and the `tryPatchHandler` function were removed. To disable tracing, set `tracesSampleRate` to `0`.
* (Express) The deprecated `patchExpressModule(options)` signature was removed. Use `patchExpressModule(moduleExports, getOptions)` instead.
* (Express) `ExpressIntegrationOptions` is no longer exported from `@sentry/core`. Import it from `@sentry/node`, which is the version `expressIntegration()` accepts.
* (Fastify) The deprecated `instrumentFastify`, `handleFastifyError`, and `setShouldHandleError` exports were removed.

### [AI Integrations](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#ai-integrations)

* The `enableTruncation` and `streamGenAiSpans` flags were removed. Gen AI spans are always streamed and never truncated now.
* The `addVercelAiProcessors` helper was removed. Add `vercelAIIntegration()` instead.
* The AI instrumentation moved from `@sentry/core` to `@sentry/server-utils`. If you imported a helper such as `instrumentOpenAiClient` or `createLangChainCallbackHandler` directly from `@sentry/core`, import it from your platform SDK (for example `@sentry/node`) or from `@sentry/server-utils`.
* Low-level provider instrumentation internals, integration-name constants, and their types are no longer exported from `@sentry/core`.

### [Profiling](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#profiling)

The legacy per-transaction profiling options were removed. Configure [session-based profiling](https://docs.sentry.io/platforms/javascript/guides/nextjs/profiling.md) with `profileSessionSampleRate` and a `profileLifecycle` of `'trace'` or `'manual'` instead. The `prune-profiler-binaries` script was removed from `@sentry/profiling-node`.

## [Package Changes](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#package-changes)

* **`@sentry/types` is no longer published.** Import all types from `@sentry/core`. The package has only re-exported from `@sentry/core` since v8.
* **`@sentry/node-core` was merged back into `@sentry/node`.** With the reduced OpenTelemetry footprint, it no longer serves a purpose. Import everything from `@sentry/node`.
* **`@sentry/tanstackstart` was removed.** Use `@sentry/tanstackstart-react`.
* **Metrics moved out of the base CDN bundle.** They ship only in the `*.logs.metrics` bundles now. On the other bundles, `Sentry.metrics.*` is a no-op that warns in debug builds.

## [Renames](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#renames)

These integrations and options kept their behavior, but changed their name.

### [The `InboundFilters` Integration Is Now `EventFilters`](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#the-inboundfilters-integration-is-now-eventfilters)

The `inboundFiltersIntegration` export was removed, and the integration reports itself as `EventFilters`. Update references by name, for example in `client.getIntegrationByName()`:

```js
// Before
Sentry.init({
  integrations: (integrations) =>
    integrations.filter((integration) => integration.name !== "InboundFilters"),
});

// After
Sentry.init({
  integrations: (integrations) =>
    integrations.filter((integration) => integration.name !== "EventFilters"),
});
```

### [The `childProcess` Integration Was Split](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#the-childprocess-integration-was-split)

`childProcessIntegration` covers `child_process`, and the new `workerThreadsIntegration` covers `worker_threads`. Both are enabled by default, so no change is needed to keep the previous behavior.

The `captureWorkerErrors` option was removed, and worker thread errors are always captured as events. To opt out, remove the integration:

```js
// Before
Sentry.init({
  integrations: [
    Sentry.childProcessIntegration({ captureWorkerErrors: false }),
  ],
});

// After
Sentry.init({
  integrations: (integrations) =>
    integrations.filter((integration) => integration.name !== "WorkerThreads"),
});
```

Note that `captureWorkerErrors: false` used to downgrade worker thread errors to a breadcrumb. That breadcrumb is gone, so removing the integration drops those errors entirely. The mechanism type also changed from `auto.child_process.worker_thread` to `auto.node.worker_threads`.

### [Other Renames](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#other-renames)

* `otlpIntegration()` was renamed to `openTelemetryIntegration()`, because it sends nothing over OTLP. It reports itself as `OpenTelemetry` instead of `OtlpIntegration`. `getOtlpTracesEndpoint()` keeps its name.
* `instrumentLangGraph` was renamed to `instrumentStateGraph`, because it only instruments the `StateGraph` class.
* The low-level `propagateTrace` option of `getHttpClientSubscriptions` and `patchHttpModuleClient` is called `tracePropagation` now, matching the `httpIntegration` option of the same name.

## [Type Changes](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#type-changes)

* Several public types that used `any` now use `unknown`, including `StackFrame`, `SamplingContext`, `SentryError`, and `User`. You may need to narrow types explicitly.
* Attribute typing and serialization were unified across the SDK.
* The `attributes` field of the `SamplingContext` passed to `tracesSampler` is required now. The SDK always provides it, so this only affects code that narrows or builds `SamplingContext` objects by hand.
* The `attributes` field of `ScopeData` is required now. This only affects code that constructs `ScopeData` objects manually: add `attributes: {}` there.
* The `endTimestamp` property was removed from `SentrySpanArguments`. Call `span.end(timestamp)` instead.
* `BrowserOptions` supports the `TransportOptions` generic now.

## [No Version Support Timeline](https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11.md#no-version-support-timeline)

Version support timelines are stressful for everybody using the SDK, so we won't be defining one. Instead, we will be applying bug fixes and features to older versions as long as there is demand.

Additionally, we hold ourselves accountable to any security issues, meaning that if any vulnerabilities are found, we will in almost all cases backport them.

It's decided on a case-by-case basis what gets backported. If you need a fix or feature in a previous version of the SDK, please reach out via a [GitHub issue](https://github.com/getsentry/sentry-javascript/issues).
