Is there an existing issue for this?
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/node
SDK Version
10.48.0
Framework Version
n/a
Link to Sentry event
No response
Reproduction Example/SDK Setup
Environment
@sentry/node: 10.48.0
@anthropic-ai/sdk: 0.87.0
braintrust: 3.8.0
Node.js: v22.13.0
Express 5.2.1
Summary
Enabling Sentry tracing (tracesSampleRate or tracesSampler) prevents Braintrust's wrapAnthropic() from tracing streamed Anthropic API responses. Non-streaming responses trace fine. Disabling Sentry tracing entirely resolves the issue. No amount of httpIntegration configuration (spans: false, ignoreOutgoingRequests) helps. It seems like the conflict is at the OpenTelemetry context propagation layer.
Root cause analysis (warning: written by Claude)
Braintrust's wrapAnthropic() proxies messages.create() and wraps it in a diagnostics_channel TracingChannel.tracePromise() call. When messages.create({ stream: true }) resolves, braintrust's asyncEnd handler patches [Symbol.asyncIterator] on the resolved Stream object to collect chunks. When iteration completes, the span is logged.
When Sentry tracing is enabled, two things interfere with this:
- SentryHttpInstrumentation (@sentry/node-core/build/cjs/integrations/http/SentryHttpInstrumentation.js:159) calls api.context.bind(api.context.active(), response) on the HTTP response stream as soon as the response event fires.
- @opentelemetry/instrumentation-http (build/src/http.js:274) does the same — api.context.bind(api.context.active(), response).
api.context.bind() on an EventEmitter (AbstractAsyncHooksContextManager.js:57-80) patches the response object's listener methods (on, once, addListener, prependListener, removeListener, off, removeAllListeners), wrapping every callback in a context-propagating wrapper and storing mappings via Symbol('OtListeners').
This mutation of the HTTP response stream — which underlies the Anthropic SDK's Stream async iterable — interferes with braintrust's ability to patch and observe the [Symbol.asyncIterator] lifecycle on the asyncEnd event. The span is created but never receives output or gets .end() called, so it silently drops.
Non-streaming works because the response is fully buffered before braintrust processes it — no async iterator involved.
What I've tried (none work)
Sentry.httpIntegration({ spans: false })
Sentry.httpIntegration({ ignoreOutgoingRequests: (url) => url.includes('api.anthropic.com') })
These configure the HTTP integration but don't prevent the OpenTelemetry context manager from binding to response streams globally.
Steps to Reproduce
Minimal reproduction using an Express server with both Sentry and Braintrust:
import * as Sentry from '@sentry/node'
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 1, // removing this fixes the issue
})
import { initLogger, wrapAnthropic } from 'braintrust'
import Anthropic from '@anthropic-ai/sdk'
import express from 'express'
initLogger({ projectId: '...', apiKey: '...' })
const app = express()
app.get('/stream', async (req, res) => {
const anthropic = wrapAnthropic(new Anthropic())
const response = await anthropic.messages.stream({
model: 'claude-haiku-4-5',
messages: [{ role: 'user', content: 'Say hello' }],
max_tokens: 100,
})
for await (const event of response) {
if ('delta' in event && 'text' in event.delta) res.write(`data: ${event.delta.text}\n\n`)
}
await response.finalMessage()
res.end()
})
app.listen(3210)
Save that to a test.ts file and run it with npx tsx ./test.ts.
- With tracesSampleRate: 1: The stream completes successfully (the client receives all chunks), but no trace appears in Braintrust.
- Without tracesSampleRate: The trace appears in Braintrust as expected.
- Without Sentry entirely: The trace appears in Braintrust as expected.
Expected Result
Sentry's OpenTelemetry context binding on HTTP response streams should not break third-party [Symbol.asyncIterator] patching on those streams. Libraries using diagnostics_channel TracingChannel to instrument streaming responses should continue to function when Sentry tracing is enabled.
Actual Result
Stream completes, but no trace appears in Braintrust.
Additional Context
No response
Priority
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it.
Is there an existing issue for this?
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/node
SDK Version
10.48.0
Framework Version
n/a
Link to Sentry event
No response
Reproduction Example/SDK Setup
Environment
Summary
Enabling Sentry tracing (tracesSampleRate or tracesSampler) prevents Braintrust's
wrapAnthropic()from tracing streamed Anthropic API responses. Non-streaming responses trace fine. Disabling Sentry tracing entirely resolves the issue. No amount of httpIntegration configuration (spans: false, ignoreOutgoingRequests) helps. It seems like the conflict is at the OpenTelemetry context propagation layer.Root cause analysis (warning: written by Claude)
Braintrust's wrapAnthropic() proxies messages.create() and wraps it in a diagnostics_channel
TracingChannel.tracePromise()call. Whenmessages.create({ stream: true })resolves, braintrust's asyncEnd handler patches[Symbol.asyncIterator]on the resolved Stream object to collect chunks. When iteration completes, the span is logged.When Sentry tracing is enabled, two things interfere with this:
api.context.bind()on an EventEmitter (AbstractAsyncHooksContextManager.js:57-80) patches the response object's listener methods (on, once, addListener, prependListener, removeListener, off, removeAllListeners), wrapping every callback in a context-propagating wrapper and storing mappings via Symbol('OtListeners').This mutation of the HTTP response stream — which underlies the Anthropic SDK's Stream async iterable — interferes with braintrust's ability to patch and observe the [Symbol.asyncIterator] lifecycle on the asyncEnd event. The span is created but never receives output or gets .end() called, so it silently drops.
Non-streaming works because the response is fully buffered before braintrust processes it — no async iterator involved.
What I've tried (none work)
These configure the HTTP integration but don't prevent the OpenTelemetry context manager from binding to response streams globally.
Steps to Reproduce
Minimal reproduction using an Express server with both Sentry and Braintrust:
Save that to a
test.tsfile and run it withnpx tsx ./test.ts.Expected Result
Sentry's OpenTelemetry context binding on HTTP response streams should not break third-party
[Symbol.asyncIterator]patching on those streams. Libraries usingdiagnostics_channelTracingChannelto instrument streaming responses should continue to function when Sentry tracing is enabled.Actual Result
Stream completes, but no trace appears in Braintrust.
Additional Context
No response
Priority
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding
+1orme too, to help us triage it.