Migrate from 10.x to 11.x for SolidStart

Learn about migrating from Sentry JavaScript SDK 10.x to 11.x.

Agent-Assisted Migration
Use curl to download and read https://docs.sentry.io/platforms/javascript/guides/solidstart/migration/v10-to-v11.md before making changes. Upgrade this project from Sentry JavaScript SDK v10 to v11. Inspect the project's Sentry packages, configuration, runtime, and framework versions, then apply every migration step that applies. Preserve the intended data collection behavior when migrating sendDefaultPii to dataCollection. Use the project's package manager to update dependencies and its existing checks to verify the migration. Summarize the changes and any remaining manual steps.
Copy this prompt into your AI agent to migrate to v11. Works with Cursor, Claude Code, Codex, and more.

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, which unlocks tracing on platforms like Vercel, Netlify and Cloudflare, but also runtimes like Bun and Deno.
  • Span streaming: Stream mode 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 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 11 has new compatibility ranges for runtimes, frameworks, and libraries.

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.

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

The same supported Node.js range applies if you use Node.js to install dependencies or build a browser project. Loading the SDK directly in a browser from a CDN does not require Node.js.

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.

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

sendDefaultPii is replaced by dataCollection, which controls each category of collected data separately. Our blog post The Data Collection Control Panel covers the thinking behind it.

Categoryv10 default (sendDefaultPii off)v11 default
userInfofalsetrue
cookiesnot collectedtrue
httpHeadersrequest + response, PII scrubbedrequest + response
httpBodiesnot collected (size only)all request/response
urlQueryParamstruetrue
genAIinputs + outputs not collectedinputs + outputs
databaseQueryDatafalsetrue
stackFrameVariablestruetrue
frameContextLines75

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 config for the categories v11 collects now, especially request and response bodies.

Keeping the v10 collection defaults
Copied
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 for the full list of categories.

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.

Copied
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.

Spans are no longer capped at 1000 per transaction, and individual payload limits are higher. See stream mode for how it works.

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:

Copied
// 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:

Copied
// 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 callback now runs as each span finishes and receives a StreamedSpanJSON object. Its fields were renamed:

Before (SpanJSON)After (StreamedSpanJSON)
descriptionname
dataattributes
opattributes['sentry.op']
timestampend_timestamp
status (string)status ('ok' or 'error')
Copied
// 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.

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.

Copied
// 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:

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

Scope tags and extra aren't carried over to streamed spans either.

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

In stream mode, span names follow the Sentry span name conventions. URLs, route parameters, and other client-supplied values are no longer part of the name:

Span opBeforeAfter
http.clientGET https://api.example.com/users/123GET api.example.com
http.serverGET /users/123GET /users/:id, or GET
pageload, navigation/users/123/users/:id, or Pageload
resource.*/assets/app.jscdn.example.com, or Resource
graphqlquery GetUserGraphQL query
queue.*my-queue process, poll my-topicprocess my-queue, receive my-topic
db.querySELECT * FROM "User" WHERE "id" = $1SELECT "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 opBeforeAfter
pageload, navigationThe route, or the raw URL path (/users/123)The route, or Pageload and Navigation
http.serverThe 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
routerFramework-specific, sometimes the raw URL (SvelteKit Route Change)The span's http.route, or Router
handlerFramework-specific, often with the request method (GET /users/:id, getUser)The span's http.route, or Request handler
graphqlThe 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_agentThe 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.serverThe 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 nameThe notification method name, or MCP notification
queue.publishIntegration-specific (publish my-exchange, send my-topic)The operation type and destination (send my-exchange), or the operation type alone
queue.processIntegration-specific (my-queue process)The operation type and destination (process my-queue), or the operation type alone
queue.receiveThe 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 nameThe 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
browser.* navigation timingThe document URLA static phase name, such as DNS lookup, Request, or Load event
functionIntegration-specific, such as serverAction/updateUser or LOADER routes/users.$idcode.function.name, such as updateUser or loader
function (Angular TraceMethod)<getUser> or <unnamed>The decorator's name, or Function execution
function (SvelteKit)The route or request pathThe wrapped function's name, such as load or GET
function (Ember route hooks)The full route nameThe hook name, such as beforeModel, model, or setupController
function (React Router route hooks)The route, raw URL path, or fetcher keyThe hook name, such as loader, action, clientLoader, or fetcher
function (NestJS @OnEvent)event user.createdThe event name, such as user.created
function (TanStack Start server functions)GET /_serverFn/abc123The function name, or serverFn until middleware resolves it
function.gcpThe HTTP method and path, or trigger typeThe function name, or Serverless function execution
function.awsThe Lambda function nameUnchanged, with Serverless function execution as the fallback
ui.mount, ui.render, ui.update, ui.unmount<UserList>, Vue <Root>, Application Render, or initThe unwrapped component name, or Component mount, Component render, Component update, or Component unmount
ui.taskrunloopUI task
ui.action.clickThe pageload or navigation span's namerouter.navigation.route.id, url.template, or http.route, in that order, or Click
ui.interaction.*The element's DOM pathThe annotated component name, or Click, Hover, Drag, or Key press
ui.webvital.lcpThe LCP element's DOM pathThe annotated component name, or Largest contentful paint
ui.webvital.clsThe first layout-shift source's DOM pathThe annotated component name, or Layout shift

In transaction mode, span names remain unchanged. 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.
Browser timing, function, and UI span details

Browser navigation timing spans use these names:

Span opName
browser.cacheCache lookup
browser.dnsDNS lookup
browser.connectConnect
browser.tls_sslTLS handshake
browser.redirectRedirect
browser.requestRequest
browser.responseResponse
browser.unload_eventUnload event
browser.dom_content_loaded_eventDOMContentLoaded event
browser.load_eventLoad event

The document URL stays on url.full in both trace lifecycles, subject to dataCollection.urlQueryParams. Update name-based filters to use that attribute.

Framework function spans keep the previous name on sentry.description. For SvelteKit, this includes wrapLoadWithSentry, wrapServerLoadWithSentry, wrapServerRouteWithSentry, and spans from SvelteKit's own tracing. Match the route on http.route, or url.template for client-side universal loads, and the request path on url.path.

GCP and Firebase function names come from FUNCTION_TARGET or K_SERVICE. The function name stays on faas.name, trigger details on gcp.function.context.*, and HTTP method and path on http.request.method and url.path. AWS Lambda spans fall back to Serverless function execution when neither the invocation context nor AWS_LAMBDA_FUNCTION_NAME provides a name. Their sentry.segment.name.source is now component in both trace lifecycles.

Component spans keep their original names on sentry.description. Vue's Application Render becomes Root, and Ember's init becomes Component mount. Update filters matching <UserList> or Vue <Root> to use the new name or the description.

Click idle spans copy router.navigation.route.id, url.template, http.route, url.path, and url.full from the route span. Event Timing and INP spans keep the DOM path on browser.web_vital.inp.target and the annotated component on ui.component_name, in both trace lifecycles. Match the target attribute if you previously filtered by DOM path.

LCP and CLS DOM paths stay on browser.web_vital.lcp.element and browser.web_vital.cls.source.1. Long task and long animation frame names remain Main UI thread blocked.

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

AreaBeforeAfter
Request handlers (Express, Koa, Fastify, Elysia, NestJS)request_handler.<library>, handler.nestjshandler
Hono app.request() in-process dispatchhono.requesthttp.server
Web-server middlewaremiddleware.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 layersrouter.express, router.koa, router.hapirouter
Hapi server extensionsserver.ext.hapimiddleware
NestJS setup and lifecycle handlersapp_creation.nestjs, request_context.nestjs, event.nestjsfunction

Framework functions

AreaBeforeAfter
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.tanstackstartfunction

Frontend and UI

AreaBeforeAfter
Frontend routingui.angular.routing, ui.sveltekit.routing, ui.ember.transitionrouter
React, Vue, and Svelte component lifecyclesui.react.mount/render/update, ui.svelte.init/update, Vue render/update/mount/create/activate/unmount/destroyui.mount, ui.render, ui.update, ui.unmount
Angular tracing decoratorsui.angular.init, ui.angular.<method>ui.mount, function
Ember route hooks, runloop, and componentsui.ember.route.<hook>, ui.ember.runloop.<queue>, ui.ember.component.render/definition/initfunction, ui.task, ui.render/ui.resolve/ui.mount
Browser paint entriespaintbrowser.paint

Databases, cache, and messaging

AreaBeforeAfter
Redis commands and connectdb.redis, db.redis.connectdb.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.clearcache.get, cache.put, cache.remove
Kafka, AMQP, and OTel-inferred messagingmessage, message.produce, message.consumequeue.publish, queue.receive, queue.process
AWS SQS and SNS messaging commandsrpcqueue.publish, queue.receive

RPC and Gen AI

AreaBeforeAfter
tRPCrpc.serverrpc
GCP gRPC callsgrpc.<service>grpc
AWS Bedrock inferencerpcgen_ai.chat, gen_ai.generate_content
Gen AI fallbacks and model metadata (Vercel AI, LangGraph)gen_ai.unknown, ai.run, gen_ai.modelsfunction

FaaS, serverless, and HTTP clients

AreaBeforeAfter
AWS Lambda functionsfunction.aws.lambdafunction.aws
GCP functionsfunction.gcp.http, function.gcp.event, function.gcp.cloud_eventfunction.gcp
Firebase functionshttp.requestfunction.gcp
Cloudflare cron, email, and workflow stepsfaas.cron, faas.email, function.step.dofunction
OTel-inferred FaaS spans (from faas.trigger)arbitrary trigger strings used verbatimhttp.server, queue.process, function
GCP HTTP clienthttp.client.<service>http.client
Prefetch HTTP requestshttp.client.prefetch, http.server.prefetchhttp.client, http.server

Casing normalized to snake_case

BeforeAfter
ui.long-taskui.long_task
ui.long-animation-frameui.long_animation_frame
browser.unloadEventbrowser.unload_event
browser.domContentLoadedEventbrowser.dom_content_loaded_event
browser.loadEventbrowser.load_event
browser.TLS/SSLbrowser.tls_ssl
browser.DNSbrowser.dns

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 attributev11 attribute
http.hostserver.address
http.flavornetwork.protocol.version
http.client_ipclient.address
http.methodhttp.request.method
http.status_codehttp.response.status_code
http.status_texthttp.response.status_text
http.schemeurl.scheme
http.user_agentuser_agent.original
http.request_content_lengthhttp.request.body.size
http.response_content_lengthhttp.response.body.size
http.decoded_response_content_lengthhttp.response.body.decoded_size
http.response_transfer_sizehttp.response.size
http.targeturl.path + url.query
http.queryurl.query
http.fragmenturl.fragment
url.same_originhttp.request.same_origin
net.host.name, net.peer.nameserver.address
net.host.ipnetwork.local.address
net.host.portnetwork.local.port
net.peer.ipnetwork.peer.address
net.peer.portnetwork.peer.port
net.transportnetwork.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.

HTTP header attribute names keep their hyphens instead of replacing them with underscores. For example, http.request.header.user_agent becomes http.request.header.user-agent. Header values are now string arrays, with one entry per value: ['gzip', 'deflate'] replaces 'gzip;deflate'.

Cookies are no longer split into attributes per cookie name. http.request.header.cookie and http.request.header.set-cookie now hold arrays of name=value entries in the order sent, such as ['session=[Filtered]', 'theme=dark']. Sensitive values remain filtered, and Set-Cookie attributes such as HttpOnly are still omitted.

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 attributev11 attribute
db.systemdb.system.name
db.namedb.namespace
db.operationdb.operation.name
db.statementdb.query.text
db.mongodb.collectiondb.collection.name
net.peer.nameserver.address
net.peer.portserver.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 attributev11 attribute
gen_ai.systemgen_ai.provider.name
gen_ai.request.available_toolsgen_ai.tool.definitions
gen_ai.tool.inputgen_ai.tool.call.arguments
gen_ai.tool.outputgen_ai.tool.call.result
gen_ai.usage.cache_creation_input_tokensgen_ai.usage.cache_creation.input_tokens
gen_ai.usage.cache_read_input_tokensgen_ai.usage.cache_read.input_tokens
gen_ai.usage.input_tokens.cachedgen_ai.usage.cache_read.input_tokens
gen_ai.usage.input_tokens.cache_writegen_ai.usage.cache_creation.input_tokens
gen_ai.usage.output_tokens.reasoninggen_ai.usage.reasoning.output_tokens
ai.pipeline.namegen_ai.pipeline.name
ai.streaminggen_ai.response.streaming
langchain.chain.namegen_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>.
  • Vue Router's navigation.route.id became router.navigation.route.id, with the same matched route name.
  • AWS Lambda spans use faas.invocation_id instead of faas.execution for the request ID, and cloud.resource_id instead of faas.id for the invoked function ARN. Lambda Invoke spans from awsIntegration also use faas.invocation_id.
  • Koa router and middleware spans no longer set koa.name. Use http.route for the route and code.function.name for the middleware handler.
  • The Cloudflare-specific sentry.cloudflare_tracer attribute is no longer set.
  • Import attribute constants from @sentry/core directly. The semanticAttributes re-export was removed.
  • sentry.source (SEMANTIC_ATTRIBUTE_SENTRY_SOURCE) was replaced by sentry.segment.name.source, which is only set on the root span.

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

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:

Copied
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.

Copied
Sentry.init({
  dsn: "___PUBLIC_DSN___",
  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.

Copied
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("___PUBLIC_DSN___"))
    ),
  ],
});

provider.register();

Sentry.init({
  dsn: "___PUBLIC_DSN___",
  // 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.

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

AI integrations now trace model invocations, tool calls, and agent invocations only. They no longer emit spans for Anthropic messages.countTokens, models.retrieve, or models.get, or LangGraph graph compilation with gen_ai.create_agent.

Update dashboards and alerts that reference these spans. LangGraph's gen_ai.invoke_agent and gen_ai.execute_tool spans are unchanged.

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.

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 instead. It works for both ESM and CommonJS apps.

Copied
# Before
node --require ./instrument.js app.js

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

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():

Copied
// 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.

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:

Copied
// 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 optionReplacement
trackIncomingRequestsAsSessionssessions
maxIncomingRequestBodySizemaxRequestBodySize
ignoreIncomingRequestBodyignoreRequestBody
dropSpansForIncomingRequestStatusCodesignoreStatusCodes
incomingRequestSpanHookonSpanCreated

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:

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

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.

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.

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

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

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

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 instead.

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.
Copied
// Restore a new session on load and on every navigation
Sentry.init({
  integrations: [Sentry.browserSessionIntegration({ lifecycle: "route" })],
});

See BrowserSession for lifecycle configuration and session health details.

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.

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.

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.

Copied
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.

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

Removed optionReplacement
_experiments.enableInteractionsinteractionsIntegration()
ignorePerformanceApiSpansuserTimingIntegration({ ignore: [...] })
trackFetchStreamPerformancefetchStreamPerformanceIntegration()
_experiments.enableStandalone*SpansRemoved. The trace lifecycle determines how CLS and LCP are sent.
Copied
// 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. Configure idleTimeout, finalTimeout, and childSpanTimeout for interaction spans on interactionsIntegration, with the same defaults as before. BrowserTracing's timeout options still control pageload and navigation spans.

BrowserTracing now automatically adds the WebVitals integration. Web Vitals changed as follows:

  • CLS and LCP are recorded as measurements on the pageload span, or as dedicated spans in stream mode.
  • LCP and CLS spans no longer set browser.web_vital.lcp.report_event or browser.web_vital.cls.report_event, including when soft-navigation reporting is disabled. Finalization timing is unchanged. Remove filters on these attributes from searches and dashboards.
  • When collected, 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.
  • The enableInp option is deprecated. To disable INP, use browserTracingIntegration({ webVitals: { ignore: ["inp"] } }). The ignore array can also contain "cls" or "lcp" to skip those vitals.
  • To configure Web Vitals, pass webVitals options to BrowserTracing, or add webVitalsIntegration() explicitly. Explicit registration takes precedence over BrowserTracing's webVitals and enableInp options.
  • Soft-navigation vitals are enabled by default in stream mode on browsers that support the Soft Navigations API. Set webVitals: { softNavigations: false } to keep one set of vitals for the page lifetime.
  • BrowserTracing starts a navigation span after a back/forward-cache restore by default. Set instrumentBfcacheRestore: false to disable it. Web Vitals for restores are collected by default in stream mode when instrumentBfcacheRestore is enabled. Set webVitals: { bfcacheNavigations: false } to disable this collection. Restore vitals carry browser.navigation.type: bfcache. Filter by that attribute to analyze restores separately from page loads, since near-instant restores can lower combined aggregates.

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

Move its fields to the top level of the SDK's build options:

Copied
// Before
sourceMapsUploadOptions: {
  org: "my-org",
  project: "my-project",
  authToken: process.env.SENTRY_AUTH_TOKEN,
  sourcemaps: { assets: ["./build/**/*"] },
}

// After
org: "my-org",
project: "my-project",
authToken: process.env.SENTRY_AUTH_TOKEN,
sourcemaps: { assets: ["./build/**/*"] },

The enabled field is gone. Use sourcemaps: { disable: true } instead of enabled: false.

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 Sentry webpack plugin requires webpack 5.1 or newer. The @sentry/bundler-plugins/webpack5 entry point was removed. Update imports to @sentry/bundler-plugins/webpack:

Copied
// Before
import { sentryWebpackPlugin } from "@sentry/bundler-plugins/webpack5";

// After
import { sentryWebpackPlugin } from "@sentry/bundler-plugins/webpack";

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:

Copied
// 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.

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

  • @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.
Copied
// 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 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.

  • The console option of breadcrumbsIntegration was removed. Console breadcrumbs come from the default consoleIntegration now. To disable them, filter out the Console integration, and if you set defaultIntegrations: false, add consoleIntegration() to keep them.
  • AI integrations are no longer available in the browser SDK. They remain available in the server-side 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:
Copied
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 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.

  • 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.

The legacy per-transaction profiling options were removed. Configure session-based profiling with profileSessionSampleRate and a profileLifecycle of 'trace' or 'manual' instead. The prune-profiler-binaries script was removed from @sentry/profiling-node.

  • @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.

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

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

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

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

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:

Copied
// 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.

  • 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.

  • 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.

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.

Was this helpful?
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").