From 1674113976b45c9a1091180702684082c5e86583 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 20 Mar 2026 11:32:19 +0100 Subject: [PATCH 1/4] set error status --- .../src/integrations/tracing/vercelai/instrumentation.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/node/src/integrations/tracing/vercelai/instrumentation.ts b/packages/node/src/integrations/tracing/vercelai/instrumentation.ts index 2dfa8657bd4c..5ac1e365c45d 100644 --- a/packages/node/src/integrations/tracing/vercelai/instrumentation.ts +++ b/packages/node/src/integrations/tracing/vercelai/instrumentation.ts @@ -9,6 +9,7 @@ import { getClient, handleCallbackErrors, SDK_VERSION, + SPAN_STATUS_ERROR, withScope, } from '@sentry/core'; import { INTEGRATION_NAME } from './constants'; @@ -263,7 +264,11 @@ export class SentryVercelAiInstrumentation extends InstrumentationBase { // So to circumvent this, we set the active span on the error object // which is picked up by the unhandledrejection handler if (error && typeof error === 'object') { - addNonEnumerableProperty(error, '_sentry_active_span', getActiveSpan()); + const activeSpan = getActiveSpan(); + addNonEnumerableProperty(error, '_sentry_active_span', activeSpan); + if (activeSpan) { + activeSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); + } } }, () => {}, From 40679eb1a5fb466b299c7d1cca94f49503091dd1 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 20 Mar 2026 15:40:28 +0100 Subject: [PATCH 2/4] . --- packages/core/src/tracing/vercel-ai/index.ts | 6 ++++++ .../src/integrations/tracing/vercelai/instrumentation.ts | 7 +------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/core/src/tracing/vercel-ai/index.ts b/packages/core/src/tracing/vercel-ai/index.ts index 1d8a27e8e3aa..086865618053 100644 --- a/packages/core/src/tracing/vercel-ai/index.ts +++ b/packages/core/src/tracing/vercel-ai/index.ts @@ -266,6 +266,12 @@ function processEndedVercelAiSpan(span: SpanJSON): void { return; } + // The Vercel AI SDK sets span status to raw error message strings. + // Normalize any non-ok status to 'internal_error'. + if (span.status && span.status !== 'ok') { + span.status = 'internal_error'; + } + renameAttributeKey(attributes, AI_USAGE_COMPLETION_TOKENS_ATTRIBUTE, GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE); renameAttributeKey(attributes, AI_USAGE_PROMPT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE); renameAttributeKey(attributes, AI_USAGE_CACHED_INPUT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_INPUT_TOKENS_CACHED_ATTRIBUTE); diff --git a/packages/node/src/integrations/tracing/vercelai/instrumentation.ts b/packages/node/src/integrations/tracing/vercelai/instrumentation.ts index 5ac1e365c45d..2dfa8657bd4c 100644 --- a/packages/node/src/integrations/tracing/vercelai/instrumentation.ts +++ b/packages/node/src/integrations/tracing/vercelai/instrumentation.ts @@ -9,7 +9,6 @@ import { getClient, handleCallbackErrors, SDK_VERSION, - SPAN_STATUS_ERROR, withScope, } from '@sentry/core'; import { INTEGRATION_NAME } from './constants'; @@ -264,11 +263,7 @@ export class SentryVercelAiInstrumentation extends InstrumentationBase { // So to circumvent this, we set the active span on the error object // which is picked up by the unhandledrejection handler if (error && typeof error === 'object') { - const activeSpan = getActiveSpan(); - addNonEnumerableProperty(error, '_sentry_active_span', activeSpan); - if (activeSpan) { - activeSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); - } + addNonEnumerableProperty(error, '_sentry_active_span', getActiveSpan()); } }, () => {}, From c279904f38465114b46b0a3975ca1909a4d36958 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 20 Mar 2026 15:47:24 +0100 Subject: [PATCH 3/4] . --- packages/core/src/tracing/vercel-ai/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/tracing/vercel-ai/index.ts b/packages/core/src/tracing/vercel-ai/index.ts index 086865618053..5458ace456c5 100644 --- a/packages/core/src/tracing/vercel-ai/index.ts +++ b/packages/core/src/tracing/vercel-ai/index.ts @@ -267,7 +267,7 @@ function processEndedVercelAiSpan(span: SpanJSON): void { } // The Vercel AI SDK sets span status to raw error message strings. - // Normalize any non-ok status to 'internal_error'. + // Any such value should be normalized to a SpanStatusType value. We pick internal_error as it is the most generic. if (span.status && span.status !== 'ok') { span.status = 'internal_error'; } From e15e33c834643721e4c02b4862ade3ddba72c172 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 20 Mar 2026 16:21:48 +0100 Subject: [PATCH 4/4] unit test --- .../lib/tracing/vercel-ai-span-status.test.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 packages/core/test/lib/tracing/vercel-ai-span-status.test.ts diff --git a/packages/core/test/lib/tracing/vercel-ai-span-status.test.ts b/packages/core/test/lib/tracing/vercel-ai-span-status.test.ts new file mode 100644 index 000000000000..dbba343eca42 --- /dev/null +++ b/packages/core/test/lib/tracing/vercel-ai-span-status.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest'; +import { addVercelAiProcessors } from '../../../src/tracing/vercel-ai'; +import type { SpanJSON } from '../../../src/types-hoist/span'; +import { getDefaultTestClientOptions, TestClient } from '../../mocks/client'; + +describe('vercel-ai span status normalization', () => { + function processSpan(status: string): string | undefined { + const options = getDefaultTestClientOptions({ tracesSampleRate: 1.0 }); + const client = new TestClient(options); + client.init(); + addVercelAiProcessors(client); + + const span: SpanJSON = { + description: 'test', + span_id: 'test-span-id', + trace_id: 'test-trace-id', + start_timestamp: 1000, + timestamp: 2000, + origin: 'auto.vercelai.otel', + status, + data: {}, + }; + + const eventProcessor = client['_eventProcessors'].find(p => p.id === 'VercelAiEventProcessor'); + const processedEvent = eventProcessor!({ type: 'transaction' as const, spans: [span] }, {}); + return (processedEvent as { spans?: SpanJSON[] })?.spans?.[0]?.status; + } + + it('normalizes raw error message status to internal_error', () => { + expect(processSpan("FileNotFoundError: The file '/nonexistent/file.txt' does not exist")).toBe('internal_error'); + }); + + it('preserves ok status', () => { + expect(processSpan('ok')).toBe('ok'); + }); +});