# Use @vercel/otel with Sentry to trace Vercel AI SDK calls

> Export Vercel AI SDK spans from your @vercel/otel pipeline to Sentry over OTLP on SDK v11, without duplicate spans or a second OpenTelemetry provider

**URL:** https://sentry.io/cookbook/vercel-ai-sdk-otel-sentry/

---

Keep your existing Vercel AI SDK and @vercel/otel setup, and export your LLM spans to Sentry over OTLP. Sentry still captures your errors and links them to the active trace.

**Time:** 15–20 minutes | **Difficulty:** Intermediate

## What You'll Learn

- Configured Sentry SDK v11 to run next to an existing @vercel/otel pipeline

- Added Sentry's OTLP endpoint as a span exporter in registerOTel

- Registered the AI SDK OpenTelemetry integration with registerTelemetry

- Found your LLM spans in Sentry's Trace Explorer and Explore → Agents

**Topics:** Tracing, AI Observability, Agent Tracing

**SDKs:** Next.js, JavaScript

## Before you start

**SDKs & packages**

- A project already using [Vercel AI SDK](https://ai-sdk.dev/) 7 with [@vercel/otel](https://www.npmjs.com/package/@vercel/otel) instrumentation

- Sentry set up with `@sentry/nextjs` 11 or later, including `sentry.server.config.ts` and `sentry.edge.config.ts`. If Sentry isn't set up yet, follow the [Next.js setup guide](https://docs.sentry.io/platforms/javascript/guides/nextjs/) first

- Node.js 22+ (required by AI SDK 7)

**Accounts & access**

- [Sentry account](https://sentry.io/signup/) with a project and DSN

**Knowledge**

- Basic familiarity with OpenTelemetry concepts (spans, span processors, exporters)

- Familiarity with the Vercel AI SDK `generateText` / `streamText` APIs

## Steps

### 1. Install the OTLP exporter and AI SDK telemetry packages

Sentry receives OpenTelemetry spans over OTLP, so you need an OTLP exporter and a span processor. AI SDK 7 also moved its OpenTelemetry support into a separate `@ai-sdk/otel` package. Keep your existing `@vercel/otel` and `ai` packages. They don't change.

```bash
npm install @ai-sdk/otel @opentelemetry/exporter-trace-otlp-http @opentelemetry/sdk-trace-base
```

[Use your own OpenTelemetry pipeline with Sentry](https://docs.sentry.io/platforms/javascript/guides/nextjs/opentelemetry/custom-setup/)

### 2. Initialize Sentry without its own OpenTelemetry setup

With this setup, `@vercel/otel` owns tracing and Sentry owns errors. Set `enableOpenTelemetrySetup: false` so Sentry doesn't try to register a second tracer provider, and leave `tracesSampleRate` unset so Sentry doesn't create its own spans for the same requests. Then add `openTelemetryIntegration()` to link Sentry errors, logs, and metrics to the active OpenTelemetry span.

```typescript
import * as Sentry from "@sentry/nextjs";

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  // @vercel/otel owns tracing, so leave tracesSampleRate unset
  enableOpenTelemetrySetup: false,
  integrations: [Sentry.openTelemetryIntegration()],
});
```

[openTelemetryIntegration reference](https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/integrations/opentelemetry/)

### 3. Export your spans to Sentry from registerOTel

`Sentry.getOtlpTracesEndpoint()` turns your DSN into the URL and auth headers of your project's OTLP endpoint. Pass it to an `OTLPTraceExporter` and add that exporter to `spanProcessors`. The `"auto"` entry keeps Vercel's default export, so your existing setup keeps working. You're **adding** Sentry as a destination, not replacing anything.

In the same `register()` function, call `registerTelemetry(new OpenTelemetry())`. In AI SDK 7, this is what makes the AI SDK emit OpenTelemetry spans. Register your OpenTelemetry provider before Sentry initializes, which is why the Sentry config is imported last.

```typescript
// instrumentation.ts
import { registerOTel } from "@vercel/otel";
import { registerTelemetry } from "ai";
import { OpenTelemetry } from "@ai-sdk/otel";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import * as Sentry from "@sentry/nextjs";

export async function register() {
  // undefined when SENTRY_DSN is missing or invalid, for example in local dev
  const sentryOtlp = Sentry.getOtlpTracesEndpoint(process.env.SENTRY_DSN ?? "");

  registerOTel({
    serviceName: "vercel-ai-otel-sentry-demo",
    spanProcessors: [
      "auto",
      ...(sentryOtlp
        ? [new BatchSpanProcessor(new OTLPTraceExporter(sentryOtlp))]
        : []),
    ],
  });

  // AI SDK 7: send AI SDK telemetry to your OpenTelemetry provider
  registerTelemetry(new OpenTelemetry());

  // Initialize Sentry after the provider is registered
  if (process.env.NEXT_RUNTIME === "edge") {
    await import("./sentry.edge.config");
  } else {
    await import("./sentry.server.config");
  }
}

export const onRequestError = Sentry.captureRequestError;
```

[Send OpenTelemetry spans to Sentry](https://docs.sentry.io/platforms/javascript/guides/nextjs/opentelemetry/custom-setup/)

### 4. Name your LLM calls with telemetry metadata

In AI SDK 7, telemetry is on by default once an integration is registered, and `experimental_telemetry` is now `telemetry`. You don't need `isEnabled: true` anymore. Add a stable `functionId` to each call so you can tell related runs apart in your traces.

The AI SDK records inputs and outputs by default, which is useful while debugging. Set `recordInputs` and `recordOutputs` to `false` if your prompts can contain sensitive data.

```typescript
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";

const { text } = await generateText({
  model: openai("gpt-5-mini"),
  prompt,
  telemetry: {
    functionId: "summarize-article",
  },
});
```

[Vercel AI SDK telemetry docs](https://ai-sdk.dev/docs/ai-sdk-core/telemetry)

### 5. Verify your AI spans in Sentry

Run your app and trigger an LLM call. Within a minute, open the [Trace Explorer](https://sentry.io/orgredirect/organizations/:orgslug/explore/traces/) in Sentry and filter by your `serviceName`. Click into a trace to see the span waterfall. The AI SDK spans sit next to the HTTP, `fetch`, and custom spans from the rest of your app, with the model, token counts, latency, and (if you record them) the full prompt and completion.

Your AI spans also show up in [Explore → Agents](https://sentry.io/orgredirect/organizations/:orgslug/explore/agents/), where you can compare models, token usage, and cost across calls.

[Agents documentation](https://docs.sentry.io/product/agents/)

## Summary

**Your LLM calls are in Sentry.**

Your OpenTelemetry pipeline is unchanged, Vercel AI SDK keeps emitting standard spans, and Sentry is now your observability backend for every model invocation.

## Pro Tips

- Use a distinct `functionId` per AI task (`summarize-article`, `classify-support-ticket`) so related runs are easy to group and regressions are easy to spot.

- Set a meaningful `serviceName` in `registerOTel`. It's the easiest way to filter spans by service in the Trace Explorer, which matters the moment you have more than one worker.

- Sentry's `tracesSampleRate` doesn't control spans you export over OTLP. Keep the default of tracing every request while you set this up, then set `traceSampler` in `registerOTel` once you trust the pipeline.

- Attach request-scoped context with `runtimeContext` (user ID, tenant, feature flag) and list the keys to send in `telemetry.includeRuntimeContext`, so you can filter traces by those attributes in Sentry. Keys you don't list stay out of your telemetry.

## Common Pitfalls

- Setting `tracesSampleRate` while `@vercel/otel` owns tracing. Sentry then creates its own spans for the same requests, and you get two copies of every operation in two pipelines that never join up.

- Leaving out `"auto"` from `spanProcessors`. That removes Vercel's default export, and your traces stop reaching any destination you already had.

- Skipping `registerTelemetry(new OpenTelemetry())` on AI SDK 7. The AI SDK no longer emits OpenTelemetry spans on its own, so your LLM calls quietly disappear from your traces.

- Initializing Sentry before `registerOTel`. Register your OpenTelemetry provider first, then import your Sentry config, as shown in Step 3.

- Expecting AI spans from the Edge runtime. The OTLP exporter in Step 3 only registers in the Node.js runtime, and Sentry's `vercelAIIntegration` is a no-op on Edge in SDK v11. Run your AI calls in Node.js route handlers.

- Recording inputs in production can send user PII or secrets to Sentry as span attributes. Set `recordInputs: false` on calls that handle regulated data, or gate it behind an environment flag.

## FAQ

**Do I have to migrate off @vercel/otel to use Sentry?**

No. `@vercel/otel` stays the owner of the OpenTelemetry pipeline. Sentry receives its spans through an extra OTLP exporter and handles errors on its own side. Your existing instrumentation keeps working.

**Why can't I use SentrySpanProcessor anymore?**

Sentry JavaScript SDK v11 removed `SentrySpanProcessor`, `SentrySampler`, and `SentryContextManager`, and replaced `skipOpenTelemetrySetup` with `enableOpenTelemetrySetup`. If you followed an older version of this recipe, swap the Sentry components in `registerOTel` for the OTLP exporter in Step 3, and remove `tracesSampleRate` from `Sentry.init()`.

**Should I use this setup or Sentry's Vercel AI integration?**

If you don't need `@vercel/otel`, use Sentry's built-in `vercelAIIntegration` instead. It's enabled by default once Sentry tracing is on and needs less setup. The [Monitor AI agent costs in Next.js](/cookbook/monitor-ai-agent-costs-nextjs/) recipe covers it. Use this recipe when OpenTelemetry already owns tracing in your app.

**Does this only work with Next.js?**

No. `@vercel/otel` and Sentry's OTLP endpoint work in any Node.js runtime. The `registerOTel` call is the same whether you're running Next.js, a standalone Node server, a worker, or a serverless function. Next.js just has a built-in `instrumentation.ts` entry point that makes the wiring convenient.

**Which Sentry SDK should I use?**

Use the SDK that matches your runtime: `@sentry/nextjs` for Next.js apps, `@sentry/node` for plain Node services, `@sentry/bun`, `@sentry/aws-serverless`, and so on. In SDK v11, every server SDK exports `getOtlpTracesEndpoint()` and `openTelemetryIntegration()`. On `@sentry/nextjs`, set `enableOpenTelemetrySetup: false` explicitly.

**Will Sentry see my existing non-AI OpenTelemetry spans too?**

Yes. The OTLP exporter sits next to Vercel's `"auto"` processors, so every span your pipeline produces (HTTP, `fetch`, database, custom) reaches Sentry as part of the same trace.

**Does recording inputs and outputs have a cost?**

Sentry bills tracing by span count, so recording inputs and outputs makes each span larger without adding spans to your quota. The bigger concern is privacy. If your prompts can contain user data, set `recordInputs` and `recordOutputs` to `false` in production, and control volume with `traceSampler`.

## Next Steps

- [solution/ai-observability](https://sentry.io/solutions/ai-observability/)

- [product/tracing](https://sentry.io/product/tracing/)

- [Monitor AI agent costs in Next.js](/cookbook/monitor-ai-agent-costs-nextjs/): A companion recipe that uses Sentry's built-in Vercel AI integration to track token usage and model costs, without @vercel/otel.

- [Add error monitoring on top](https://docs.sentry.io/platforms/javascript/guides/nextjs/): You already have Sentry installed. Make sure unhandled errors and API failures are captured with the same SDK.

---

*Source: [sentry.io/cookbook/vercel-ai-sdk-otel-sentry/](https://sentry.io/cookbook/vercel-ai-sdk-otel-sentry/)*
