#3
Multi-line JSON WebSocket messages break SSE framing
Bug
The onMessage handler in createWebSocketFetch wraps each WebSocket message as a single SSE data: line:
function onMessage(data) {
const text = data.toString();
controller.enqueue(encoder.encode(`data: ${text}\n\n`));
}When OpenAI sends pretty-printed (multi-line) JSON over WebSocket, the newlines inside the JSON break SSE framing. The downstream EventSourceParserStream splits on newlines, so a message like:
{
"type": "response.created",
"response": { ... }
}becomes:
data: {
"type": "response.created",
"response": { ... }
}
The SSE parser sees data: { as the only data line, dispatches an event with data {, and JSON parsing fails:
[AI_JSONParseError]: JSON parsing failed: Text: {.
Expected property name or '}' in JSON at position 1Reproduction
- Use
createWebSocketFetch()with@ai-sdk/openaiandstreamText - Include a provider-defined tool like
openai.tools.shell()(forces Responses API path) - Use
gpt-5-mini, which sends multi-line JSON in some WebSocket responses gpt-5.4sends compact single-line JSON so it doesn't trigger the issue
Suggested fix
Use multi-line SSE data format so newlines in the payload are preserved:
function onMessage(data) {
const text = data.toString();
const lines = text.split('\n').map(line => `data: ${line}`).join('\n');
controller.enqueue(encoder.encode(`${lines}\n\n`));
}This follows the SSE spec — multiple data: lines are concatenated with \n by the parser, reconstructing the original multi-line content.
Confirming this in production, and flagging that the fix appears to be merged but not published.
We hit the identical error on the Responses API over WS:
It reaches the user as a 500, because the parse failure surfaces inside
streamTextwith no partial content, so our error-fallback logic can't distinguish it from a hard stream failure and rethrows.One extra data point on triggering conditions: for us it is bursty rather than constant — 21 occurrences in one hour, 9 in another, then none for two days. That's consistent with the report that only some models/events pretty-print, rather than it being deterministic per model.
The reason I'm commenting: the fix in bb59a5e (#2) looks correct, but it doesn't seem to have made it to npm.
bb59a5e is dated 2026-05-15, about a month after that publish. Unpacking the current tarball confirms the published build still has the single-line framing:
No
split(/\r?\n/)and noresponse.failed/response.incompletehandling in the shipped artifact.So anyone installing from npm today still gets the bug, and there's no version to upgrade to — we've had to vendor the module locally to pick up the fix. A patch release (1.0.1) would let us drop that.
Happy to test a release candidate against our workload if useful.