From 00ff5b5ac147beac5dce87f084062f8ce2969cab Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 8 Apr 2026 19:26:56 +0200 Subject: [PATCH 1/2] fix(deno): Avoid inferring invalid span op from Deno tracer --- .../e2e-tests/test-applications/deno/tests/ai.test.ts | 2 +- .../test-applications/deno/tests/transactions.test.ts | 3 --- packages/deno/src/opentelemetry/tracer.ts | 8 ++++---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/deno/tests/ai.test.ts b/dev-packages/e2e-tests/test-applications/deno/tests/ai.test.ts index d0b824bf9b2f..ce55597a1996 100644 --- a/dev-packages/e2e-tests/test-applications/deno/tests/ai.test.ts +++ b/dev-packages/e2e-tests/test-applications/deno/tests/ai.test.ts @@ -32,7 +32,7 @@ test('should create AI pipeline spans with Vercel AI SDK', async ({ baseURL }) = (span: any) => span.op === 'gen_ai.invoke_agent' || span.op === 'gen_ai.generate_content' || - span.op === 'otel.span' || + !span.op || span.description?.includes('ai.generateText'), ); diff --git a/dev-packages/e2e-tests/test-applications/deno/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/deno/tests/transactions.test.ts index 3cd0892cebdc..d3efc44216b9 100644 --- a/dev-packages/e2e-tests/test-applications/deno/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/deno/tests/transactions.test.ts @@ -33,7 +33,6 @@ test('Sends transaction with OTel tracer.startSpan despite pre-existing provider expect.arrayContaining([ expect.objectContaining({ description: 'test-otel-span', - op: 'otel.span', origin: 'manual', }), ]), @@ -53,7 +52,6 @@ test('Sends transaction with OTel tracer.startActiveSpan', async ({ baseURL }) = expect.arrayContaining([ expect.objectContaining({ description: 'test-otel-active-span', - op: 'otel.span', origin: 'manual', }), ]), @@ -77,7 +75,6 @@ test('OTel span appears as child of Sentry span (interop)', async ({ baseURL }) }), expect.objectContaining({ description: 'otel-child', - op: 'otel.span', origin: 'manual', }), ]), diff --git a/packages/deno/src/opentelemetry/tracer.ts b/packages/deno/src/opentelemetry/tracer.ts index 7bc704446d37..bdd86bde6a8d 100644 --- a/packages/deno/src/opentelemetry/tracer.ts +++ b/packages/deno/src/opentelemetry/tracer.ts @@ -43,7 +43,7 @@ class SentryDenoTracer implements Tracer { attributes: { ...options?.attributes, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op, + ...(op ? { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op } : {}), 'sentry.deno_tracer': true, }, }); @@ -77,7 +77,7 @@ class SentryDenoTracer implements Tracer { attributes: { ...opts.attributes, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op, + ...(op ? { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op } : {}), 'sentry.deno_tracer': true, }, }; @@ -96,7 +96,7 @@ class SentryDenoTracer implements Tracer { return startSpanManual(spanOpts, callback) as ReturnType; } - private _mapSpanKindToOp(kind?: SpanKind): string { + private _mapSpanKindToOp(kind?: SpanKind): string | undefined { switch (kind) { case SpanKind.CLIENT: return 'http.client'; @@ -107,7 +107,7 @@ class SentryDenoTracer implements Tracer { case SpanKind.CONSUMER: return 'message.consume'; default: - return 'otel.span'; + return undefined; } } } From c3468bf8d8839dc1fd8526ef0eb901eed073fc77 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 10 Apr 2026 12:17:01 +0200 Subject: [PATCH 2/2] harden test --- .../test-applications/deno/tests/ai.test.ts | 24 +++++++++++++++---- .../deno/tests/transactions.test.ts | 10 ++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/deno/tests/ai.test.ts b/dev-packages/e2e-tests/test-applications/deno/tests/ai.test.ts index ce55597a1996..9d849a33224b 100644 --- a/dev-packages/e2e-tests/test-applications/deno/tests/ai.test.ts +++ b/dev-packages/e2e-tests/test-applications/deno/tests/ai.test.ts @@ -28,13 +28,27 @@ test('should create AI pipeline spans with Vercel AI SDK', async ({ baseURL }) = // Due to the AI SDK monkey-patching limitation (https://github.com/vercel/ai/pull/6716), // only explicitly opted-in calls produce telemetry spans. // The explicitly enabled call (experimental_telemetry: { isEnabled: true }) should produce spans. - const aiSpans = spans.filter( - (span: any) => + const aiSpans = spans.filter((span: any) => { + if ( span.op === 'gen_ai.invoke_agent' || span.op === 'gen_ai.generate_content' || - !span.op || - span.description?.includes('ai.generateText'), - ); + span.op === 'gen_ai.execute_tool' + ) { + return true; + } + // Processed Vercel AI spans (incl. cases where OTel kind no longer maps to a generic `op`) + if (span.origin === 'auto.vercelai.otel') { + return true; + } + // Raw Vercel AI OTel span names / attributes before or without full Sentry mapping + if (typeof span.description === 'string' && span.description.startsWith('ai.')) { + return true; + } + if (span.data?.['ai.operationId'] != null || span.data?.['ai.pipeline.name'] != null) { + return true; + } + return false; + }); // We expect at least one AI-related span from the explicitly enabled call expect(aiSpans.length).toBeGreaterThanOrEqual(1); diff --git a/dev-packages/e2e-tests/test-applications/deno/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/deno/tests/transactions.test.ts index d3efc44216b9..19077bb76b75 100644 --- a/dev-packages/e2e-tests/test-applications/deno/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/deno/tests/transactions.test.ts @@ -37,6 +37,11 @@ test('Sends transaction with OTel tracer.startSpan despite pre-existing provider }), ]), ); + + const otelSpan = transaction.spans!.find((s: any) => s.description === 'test-otel-span'); + expect(otelSpan).toBeDefined(); + // INTERNAL (and other unmapped) kinds must not get a synthetic `otel.span` op + expect(otelSpan!.op).toBeUndefined(); }); test('Sends transaction with OTel tracer.startActiveSpan', async ({ baseURL }) => { @@ -56,6 +61,10 @@ test('Sends transaction with OTel tracer.startActiveSpan', async ({ baseURL }) = }), ]), ); + + const otelSpan = transaction.spans!.find((s: any) => s.description === 'test-otel-active-span'); + expect(otelSpan).toBeDefined(); + expect(otelSpan!.op).toBeUndefined(); }); test('OTel span appears as child of Sentry span (interop)', async ({ baseURL }) => { @@ -84,4 +93,5 @@ test('OTel span appears as child of Sentry span (interop)', async ({ baseURL }) const sentrySpan = transaction.spans!.find((s: any) => s.description === 'sentry-parent'); const otelSpan = transaction.spans!.find((s: any) => s.description === 'otel-child'); expect(otelSpan!.parent_span_id).toBe(sentrySpan!.span_id); + expect(otelSpan!.op).toBeUndefined(); });