Repository navigation
OpenAI integration (v11) breaks chat.completions.parse() / responses.parse(): "Body is unusable: Body has already been read" #24773
Description
Is there an existing issue for this?
- I have checked for existing issues
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/node (also reproduced through @sentry/nextjs 11.0.0 with Turbopack)
SDK Version
11.0.0
Framework Version
openai 7.23.0, Node.js 24.20.0
Steps to Reproduce
With the default openAIIntegration active, client.chat.completions.parse() and client.responses.parse() always throw TypeError: Body is unusable: Body has already been read. create() works.
Self-contained repro (no network, fake fetch):
// repro.mjs — run with `node repro.mjs`
import * as Sentry from '@sentry/node';
Sentry.init({ dsn: 'https://[email protected]/0', tracesSampleRate: 1.0 });
const { default: OpenAI } = await import('openai');
const body = JSON.stringify({
id: 'x', object: 'chat.completion', created: 0, model: 'gpt-5-mini',
choices: [{ index: 0, finish_reason: 'stop', message: { role: 'assistant', content: '{"a":1}', refusal: null } }],
});
const client = new OpenAI({
apiKey: 'test',
fetch: async () => new Response(body, { headers: { 'content-type': 'application/json' } }),
});
const params = {
model: 'gpt-5-mini',
messages: [{ role: 'user', content: 'hi' }],
response_format: { type: 'json_schema', json_schema: { name: 'x', schema: { type: 'object' } } },
};
await Sentry.startSpan({ name: 'repro' }, async () => {
try { await client.chat.completions.create(params); console.log('create ok'); }
catch (e) { console.log('create FAIL:', e.message); }
try { await client.chat.completions.parse(params); console.log('parse ok'); }
catch (e) { console.log('parse FAIL:', e.message); }
});| Setup | create() |
parse() |
|---|---|---|
| No Sentry | ok | ok |
Sentry.init defaults |
ok | FAIL: Body is unusable: Body has already been read |
Sentry.init with integrations: (d) => d.filter((i) => i.name !== 'OpenAI') |
ok | ok |
client.responses.parse() fails the same way.
Expected Result
parse() resolves with the parsed completion, the same as without Sentry.
Actual Result
TypeError: Body is unusable: Body has already been read
at consumeBody (node:internal/deps/undici/undici)
at _Response.text (node:internal/deps/undici/undici)
at defaultParseResponse (openai/internal/parse.mjs)
at OpenAI.parseResponseWithTimeout (openai/client.mjs)
...
Analysis
The orchestrion wrapPromise transform wraps Completions.create / Responses.create. For non-native thenables it side-chains promise.then(...) and returns the original promise, so that APIPromise methods such as withResponse() stay available.
APIPromise parses lazily, though: calling .then() starts parse(), which reads response.text(). The SDK's parse() helpers are create(body)._thenUnwrap(transform), and in openai 7.x _thenUnwrap builds a new APIPromise over the same underlying Response, with its own parse step. Sentry's side-chained .then reads the body first, so the promise the caller actually awaits then reads a consumed body.
The same would apply to any other SDK helper built on _thenUnwrap over an instrumented create.
Workaround: await client.chat.completions.create(params) and pass the result to parseChatCompletion(completion, params) from openai/lib/parser (the function parse() uses internally). There is then only one APIPromise, so Sentry's .then and the caller share its cached parse. Disabling the OpenAI integration also works.
Additional Context
A fix probably needs the instrumentation to observe the result without triggering the APIPromise's parse, for example by hooking the parse result instead of calling .then() on the returned promise, or by also covering _thenUnwrap.
Metadata
Metadata
Assignees
Projects
- StatusShow more project fieldsNo status
Activity
linear-code commentedon Sep 26, 2026
JS-3825
ihsraham commentedon Sep 28, 2026
I reproduced both parse failures with OpenAI 7.23.0 on Node 22.22.2, using published Sentry 11.0.0 and develop at
bd3ce5f. Fake fetch, no service calls. Both pass without Sentry or with the OpenAI integration disabled;create()passes throughout. I also reproduced it with the fixture's OpenAI 5.18.1.The injected wrapper starts parsing, then the helper tries to read the same response again. A no-op diagnostics subscriber reproduces the failure even with the OpenAI integration removed, so disabling output recording alone won't address it.
I'd like to take this, starting with regressions for both parse APIs while preserving
withResponse(), raw-response access and normal streaming. Is anyone already working on it, and would you prefer an OpenAI-specific fix here or a coordinated change to the shared promise wrapper?nicohrubec commentedon Sep 28, 2026
Hey, thanks for reporting! We'll get a fix out.
@ihsraham Thanks for offering, but I am already working on a fix.
fix(server-utils): Preserve response bodies for OpenAI parse helpers (#…
nicohrubec commentedon Oct 1, 2026
Merged a fix, we'll try to get this released today.
github-actions commentedon Oct 1, 2026
A PR closing this issue has just been released 🚀
This issue was referenced by PR #24783, which was included in the 11.2.0 release.