---
title: "Vercel AI"
description: "Adds instrumentation for Vercel AI SDK."
url: https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai/
---

# Vercel AI | Sentry for Cloudflare

*Import name: `Sentry.vercelAIIntegration`*

The `vercelAIIntegration` adds instrumentation for the [`ai`](https://www.npmjs.com/package/ai) SDK by Vercel to capture spans using the [AI SDK's built-in telemetry](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry).

Don't use the AI SDK's `registerTelemetry` API (AI SDK v7 and above) together with this integration. `vercelAIIntegration` already instruments the AI SDK, so registering telemetry separately produces duplicate spans.

## [Runtime Differences](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#runtime-differences)

Cloudflare Workers can't load OpenTelemetry instrumentation. On both entrypoints, Sentry reads the spans the AI SDK emits on its own instead of patching your call sites, so you must pass `experimental_telemetry` on every call. The [`@sentry/cloudflare/nodejs_compat`](https://docs.sentry.io/platforms/javascript/guides/cloudflare/features/nodejs-compat.md) entrypoint adds the Node.js APIs the AI SDK v7 telemetry channel needs.

Your entrypoint decides the rest:

|                           | `@sentry/cloudflare` | `@sentry/cloudflare/nodejs_compat` |
| ------------------------- | -------------------- | ---------------------------------- |
| Record inputs and outputs | Per call only        | Integration or per call            |
| AI SDK v7                 | Not supported        | Supported                          |
| Minimum Sentry SDK        | `10.6.0`             | `10.64.0`                          |

## [Setup](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#setup)

### [AI SDK v7](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#ai-sdk-v7)

Use the [`@sentry/cloudflare/nodejs_compat`](https://docs.sentry.io/platforms/javascript/guides/cloudflare/features/nodejs-compat.md) entrypoint and enable the Wrangler `nodejs_compat` flag:

```javascript
import * as Sentry from "@sentry/cloudflare/nodejs_compat";

export default Sentry.withSentry(
  (env) => ({
    dsn: "https://<key>@o<orgId>.ingest.sentry.io/<projectId>",
    tracesSampleRate: 1.0,
    integrations: [Sentry.vercelAIIntegration()],
  }),
  {
    async fetch(request, env, ctx) {
      /* your worker */
    },
  },
);
```

*Other available variations of the above snippet: jsonc*

### [AI SDK v6 and Below](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#ai-sdk-v6-and-below)

Import `@sentry/cloudflare` instead:

```javascript
import * as Sentry from "@sentry/cloudflare";

export default Sentry.withSentry(
  (env) => ({
    dsn: "https://<key>@o<orgId>.ingest.sentry.io/<projectId>",
    tracesSampleRate: 1.0,
    integrations: [Sentry.vercelAIIntegration()],
  }),
  {
    async fetch(request, env, ctx) {
      /* your worker */
    },
  },
);
```

On both entrypoints, adding the integration is not enough on its own. Cloudflare can't patch your call sites, so you must also pass `experimental_telemetry` on every call — including on `nodejs_compat`. See [Turn on telemetry](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#turn-on-telemetry).

## [Record Inputs and Outputs](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#record-inputs-and-outputs)

Prompts and completions are not captured by default, because they usually contain user data. Turn recording on with `recordInputs` and `recordOutputs`.

Sentry resolves both settings in this order, and stops at the first one that is set:

1. The integration option — applies to every call.
2. The call's `experimental_telemetry` — applies to that call.
3. [`dataCollection.genAI`](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/options.md#dataCollection) — applies to every call.

The integration option wins over the call, not the other way around. If you set `recordInputs: false` on the integration, no call site can turn it back on.

### [`@sentry/cloudflare/nodejs_compat`](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#sentrycloudflarenodejs_compat)

Set the options on the integration to cover every call:

```javascript
import * as Sentry from "@sentry/cloudflare/nodejs_compat";

export default Sentry.withSentry(
  (env) => ({
    dsn: "https://<key>@o<orgId>.ingest.sentry.io/<projectId>",
    tracesSampleRate: 1.0,
    integrations: [
      Sentry.vercelAIIntegration({
        recordInputs: true,
        recordOutputs: true,
      }),
    ],
  }),
  {
    async fetch(request, env, ctx) {
      /* your worker */
    },
  },
);
```

### [`@sentry/cloudflare`](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#sentrycloudflare)

The default entrypoint ignores `recordInputs` and `recordOutputs` on the integration. It accepts no error and logs no warning — your prompts are simply missing. Set both per call.

```javascript
const result = await generateText({
  model: openai("gpt-4o"),
  experimental_telemetry: {
    isEnabled: true,
    recordInputs: true,
    recordOutputs: true,
  },
});
```

`dataCollection: {}` also turns recording on, but it opts you into the SDK's permissive defaults for every other category too: user identity, cookies, headers, HTTP bodies, query parameters, and stack-frame locals. Prefer the integration and per-call options above unless you want all of it. See [`dataCollection`](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/options.md#dataCollection).

## [Configure individual calls](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#configure-individual-calls)

Every instrumented `ai` function takes an `experimental_telemetry` object. Use it to control one call instead of all of them. For the full list of fields, see the [AI SDK telemetry metadata docs](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry#telemetry-metadata).

### [Turn on Telemetry](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#turn-on-telemetry)

Set `isEnabled` to `true` on every instrumented call. Without it, the AI SDK emits no spans and Sentry has nothing to capture:

```javascript
const result = await generateText({
  model: openai("gpt-4o"),
  experimental_telemetry: { isEnabled: true },
});
```

For `ToolLoopAgent`, set it on the constructor instead. See [ToolLoopAgent](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#toolloopagent).

### [Skip a Call](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#skip-a-call)

To capture no span for one call, set `isEnabled` to `false`:

```javascript
const result = await generateText({
  model: openai("gpt-4o"),
  experimental_telemetry: { isEnabled: false },
});
```

### [Identify Your Call Sites](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#identify-your-call-sites)

Spans carry the AI SDK function name, not yours, so a trace with several `generateText` calls is hard to read. Set `functionId` to label the call site. It appears on the span as `gen_ai.function_id`:

```javascript
const result = await generateText({
  model: openai("gpt-4o"),
  experimental_telemetry: {
    functionId: "summarize-ticket",
  },
});
```

### [ToolLoopAgent](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#toolloopagent)

The integration captures spans for the [`ToolLoopAgent`](https://ai-sdk.dev/docs/agents/overview#toolloopagent-class) class. Each call to `generate()` or `stream()` creates an agent span, with the individual LLM requests and tool executions as child spans.

`ToolLoopAgent` takes its telemetry settings on the constructor, not on `generate()` or `stream()`:

```javascript
const agent = new ToolLoopAgent({
  model: openai("gpt-4o"),
  tools: {
    /* ... */
  },
  experimental_telemetry: {
    isEnabled: true,
    functionId: "weather-agent",
  },
});

const result = await agent.generate({
  prompt: "What is the weather in San Francisco?",
});
```

## [Options](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#options)

Pass these to `Sentry.vercelAIIntegration()`. The default `@sentry/cloudflare` entrypoint accepts `enableTruncation` only; [`nodejs_compat`](https://docs.sentry.io/platforms/javascript/guides/cloudflare/features/nodejs-compat.md) also accepts `recordInputs` and `recordOutputs`. Neither has module detection to override, so `force` does not apply.

### [`enableTruncation`](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#enabletruncation)

*Type: `boolean`*

Truncates recorded input messages so large payloads stay within span size limits. Affects inputs only, not outputs.

Defaults to `true`.

```javascript
import * as Sentry from "@sentry/cloudflare";

export default Sentry.withSentry(
  (env) => ({
    dsn: "https://<key>@o<orgId>.ingest.sentry.io/<projectId>",
    integrations: [
      Sentry.vercelAIIntegration({ enableTruncation: false }),
    ],
  }),
  {
    async fetch(request, env, ctx) {
      /* your worker */
    },
  },
);
```

### [`recordInputs`](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#recordinputs)

*Type: `boolean`*

Records inputs to the `ai` function call. See [Record inputs and outputs](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#record-inputs-and-outputs) for the full resolution order and the per-call alternative.

### [`recordOutputs`](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#recordoutputs)

*Type: `boolean`*

Records outputs from the `ai` function call. See [Record inputs and outputs](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#record-inputs-and-outputs) for the full resolution order and the per-call alternative.

## [Supported Operations](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#supported-operations)

Spans are captured for these `ai` functions. Pass `experimental_telemetry` to each one, as described in [Turn on telemetry](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#turn-on-telemetry):

* `generateText()`
* `streamText()`
* `generateObject()`
* `streamObject()`
* `embed()`
* `embedMany()`
* `rerank()`

Plus `generate()` and `stream()` on [`ToolLoopAgent`](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#toolloopagent).

## [Supported Versions](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#supported-versions)

* `ai`: `>=3.0.0 <=7`

- Sentry SDK: `10.6.0`+ with `@sentry/cloudflare`
- Sentry SDK: `10.64.0`+ with `@sentry/cloudflare/nodejs_compat`, required for `ai` v7

## [Troubleshooting](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#troubleshooting)

Why are my prompts and completions missing?

Recording is off unless you turn it on. Check, in order:

1. `recordInputs` and `recordOutputs` are set. See [Record inputs and outputs](https://docs.sentry.io/platforms/javascript/guides/cloudflare/configuration/integrations/vercelai.md#record-inputs-and-outputs).
2. You set them in a place the runtime reads. Some runtimes ignore the integration options and take them per call only.
3. No integration option is overriding your per-call value. The integration option wins.
