Skip to content

Commit 3965bd0

Browse files
chargomeclaude
andcommitted
fix(v10/nextjs): Register Vercel AI span processors on Next.js
Next.js bundles `ai`, so the module is never patched and the `Modules` detection fallback misses workspace and transitive deps, leaving the AI SDK's raw `ai.*` spans unprocessed. Register the processors in `init` after the client exists, so an explicitly constructed `vercelAIIntegration()` cannot override it, and make `addVercelAiProcessors` idempotent. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
1 parent 2c4ca38 commit 3965bd0

8 files changed

Lines changed: 38 additions & 5 deletions

File tree

‎dev-packages/e2e-tests/test-applications/nextjs-16/app/ai-error-test/page.tsx‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { generateText } from 'ai';
2-
import { MockLanguageModelV1 } from 'ai/test';
1+
import { generateText, MockLanguageModelV1 } from 'ai-helper';
32
import { z } from 'zod';
43
import * as Sentry from '@sentry/nextjs';
54

‎dev-packages/e2e-tests/test-applications/nextjs-16/app/ai-test/page.tsx‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { generateText } from 'ai';
2-
import { MockLanguageModelV1 } from 'ai/test';
1+
import { generateText, MockLanguageModelV1 } from 'ai-helper';
32
import { z } from 'zod';
43
import * as Sentry from '@sentry/nextjs';
54

‎dev-packages/e2e-tests/test-applications/nextjs-16/package.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@
2222
"test:assert": "pnpm test:prod && pnpm test:dev",
2323
"test:assert-webpack": "pnpm test:prod && pnpm test:dev-webpack"
2424
},
25+
"//ai": "`ai` is intentionally a transitive dep (via `ai-helper`) so the `Modules` integration cannot discover it — that is the code path the AI tests cover. Adding `ai` back here would silently weaken them.",
2526
"dependencies": {
2627
"@sentry/nextjs": "file:../../packed/sentry-nextjs-packed.tgz",
2728
"@sentry/core": "file:../../packed/sentry-core-packed.tgz",
2829
"@vercel/queue": "^0.1.3",
29-
"ai": "^3.0.0",
30+
"ai-helper": "file:./vendor/ai-helper",
3031
"import-in-the-middle": "^2",
3132
"next": "16.2.3",
3233
"react": "19.1.0",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { generateText } from 'ai';
2+
export { MockLanguageModelV1 } from 'ai/test';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { generateText } from 'ai';
2+
export { MockLanguageModelV1 } from 'ai/test';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "ai-helper",
3+
"version": "0.0.0",
4+
"private": true,
5+
"type": "module",
6+
"main": "index.mjs",
7+
"types": "index.d.ts",
8+
"dependencies": {
9+
"ai": "^3.0.0"
10+
}
11+
}

‎packages/core/src/tracing/vercel-ai/index.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,10 +513,20 @@ function processGenerateSpan(span: Span, name: string, attributes: SpanAttribute
513513
}
514514
}
515515

516+
const CLIENTS_WITH_VERCEL_AI_PROCESSORS = new WeakSet<Client>();
517+
516518
/**
517519
* Add event processors to the given client to process Vercel AI spans.
520+
*
521+
* Idempotent: both the integration and the Next.js SDK register these, and duplicate hooks would
522+
* process every span twice.
518523
*/
519524
export function addVercelAiProcessors(client: Client): void {
525+
if (CLIENTS_WITH_VERCEL_AI_PROCESSORS.has(client)) {
526+
return;
527+
}
528+
CLIENTS_WITH_VERCEL_AI_PROCESSORS.add(client);
529+
520530
client.on('spanStart', onVercelAiSpanStart);
521531
// Note: We cannot do this on `spanEnd`, because the span cannot be mutated anymore at this point
522532
client.addEventProcessor(Object.assign(vercelAiEventProcessor, { id: 'VercelAiEventProcessor' }));

‎packages/nextjs/src/server/index.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { HTTP_TARGET, URL_QUERY } from '@sentry/conventions/attributes';
55
import type { EventProcessor } from '@sentry/core';
66
import {
7+
addVercelAiProcessors,
78
applySdkMetadata,
89
debug,
910
getClient,
@@ -210,6 +211,14 @@ export function init(options: NodeOptions): NodeClient | undefined {
210211

211212
const client = nodeInit(opts);
212213

214+
// Next.js bundles `ai`, so the integration can neither patch the module nor detect it via `Modules`
215+
// (which only sees the app's own `package.json`, missing workspace and transitive deps). Register
216+
// the processors here instead — after init, so an explicitly constructed `vercelAIIntegration()`
217+
// can't override the default back to the broken behavior.
218+
if (client?.getIntegrationByName('VercelAI')) {
219+
addVercelAiProcessors(client);
220+
}
221+
213222
client?.on('beforeSampling', ({ spanAttributes }, samplingDecision) => {
214223
// There are situations where the Next.js Node.js server forwards requests for the Edge Runtime server (e.g. in
215224
// middleware) and this causes spans for Sentry ingest requests to be created. These are not exempt from our tracing

0 commit comments

Comments
 (0)