Problem
anthropicAIIntegration starts a gen_ai.chat span for messages.create({ stream: true }) but never ends it when the caller takes the raw Response via .asResponse() (or .withResponse()) instead of iterating the SDK's Stream. The span is silently dropped — no error, no partial data.
Cause
packages/server-utils/src/integrations/anthropic.ts:147 — wrapStreamResult checks isAsyncIterable(result). With .asResponse() the result is a raw Response, so the check fails and it returns false, meaning "end via beforeSpanEnd". beforeSpanEnd never fires, so nothing ends the span.
Repro
Two identical streaming calls, one per consumption style:
const client = new Anthropic({ apiKey: 'sk-fake', baseURL: 'http://127.0.0.1:3999' });
// A: raw Response
const res = await client.messages.create({ ...params, stream: true }).asResponse();
for await (const _ of res.body) {}
// B: SDK async iterable
const stream = await client.messages.create({ ...params, stream: true });
for await (const _ of stream) {}
Span lifecycle:
[spanStart] chat claude-haiku-4-5 <- A
[spanStart] chat claude-haiku-4-5 <- B
[spanEnd] chat claude-haiku-4-5 <- only one ends
Impact
Any consumer using .asResponse()/.withResponse() on a streaming call loses the span. Found via @flue/runtime, whose @earendil-works/pi-ai layer calls client.messages.create({ ...params, stream: true }, opts).asResponse() — a Flue app currently reports only a raw http.client span for every LLM call. Not Flue-specific.
Verified on @sentry/node@11.0.0-beta.2, @anthropic-ai/sdk@0.91.1, Node 24.11.1.
Problem
anthropicAIIntegrationstarts agen_ai.chatspan formessages.create({ stream: true })but never ends it when the caller takes the rawResponsevia.asResponse()(or.withResponse()) instead of iterating the SDK'sStream. The span is silently dropped — no error, no partial data.Cause
packages/server-utils/src/integrations/anthropic.ts:147—wrapStreamResultchecksisAsyncIterable(result). With.asResponse()the result is a rawResponse, so the check fails and it returnsfalse, meaning "end viabeforeSpanEnd".beforeSpanEndnever fires, so nothing ends the span.Repro
Two identical streaming calls, one per consumption style:
Span lifecycle:
Impact
Any consumer using
.asResponse()/.withResponse()on a streaming call loses the span. Found via@flue/runtime, whose@earendil-works/pi-ailayer callsclient.messages.create({ ...params, stream: true }, opts).asResponse()— a Flue app currently reports only a rawhttp.clientspan for every LLM call. Not Flue-specific.Verified on
@sentry/node@11.0.0-beta.2,@anthropic-ai/sdk@0.91.1, Node 24.11.1.