getsentry / getsentry/sentry-javascript
Anthropic gen_ai span never ends when a streaming call is consumed via .asResponse()
- Vorherrschende Sprache
- TypeScript
- Sterne
- 8.7k
- Forks
- 1.8k
- Ø Merge
- 1 T. 17 Std.
- Gemergte PRs (30 T.)
- 523
Beschreibung
### 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:
```js
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.
Beitragsleitfaden
Bewertung
Dieses Issue wurde noch nicht bewertet.