moeru-ai/airi · warning
Skipping malformed SSE chunk from OpenRouter audio stream:
Error message
Skipping malformed SSE chunk from OpenRouter audio stream:
What it means
The OpenRouter audio speech provider parses a streamed response as server-sent events and JSON.parse()s each data line to extract base64 PCM audio chunks. When a line is not valid JSON (or does not match the expected shape it is skipped), the parse error is caught per-chunk and logged with this warning; the rest of the stream continues. This is loss-tolerant by design but can hide truncated output.
Source
Thrown at packages/stage-ui/src/libs/providers/providers/openrouter-audio-speech/index.ts:76
if (!line.startsWith('data: '))
continue
const data = line.slice('data: '.length).trim()
if (data === '[DONE]') {
done = true
break
}
try {
const event = JSON.parse(data) as {
choices?: Array<{ delta?: { audio?: { data?: string } } }>
}
const audio = event.choices?.[0]?.delta?.audio?.data
if (audio)
chunks.push(audio)
}
catch (error) {
console.warn('Skipping malformed SSE chunk from OpenRouter audio stream:', data, error)
}
}
}
return chunks
}
function decodeBase64Pcm(chunks: string[]) {
const binary = atob(chunks.join(''))
const bytes = new Uint8Array(binary.length)
for (let index = 0; index < binary.length; index++)
bytes[index] = binary.charCodeAt(index)
return bytes
}
function createAudioFetch(apiKey: string, baseUrl: string, model: string) {
return async (_input: RequestInfo | URL, init?: RequestInit) => {
if (!init?.body || typeof init.body !== 'string')View on GitHub (pinned to 677329427f)
Solutions
- Log the skipped line (already included) and check whether it is '[DONE]' — if so, filter it explicitly before JSON.parse.
- Disable proxy buffering for the SSE route (proxy_buffering off; flush immediately) so chunks arrive intact.
- If many chunks are skipped, compare the raw stream (devtools) with what the client sees to find the mangling hop.
- Verify the model actually supports audio output via OpenRouter's /api/v1 chat completions with modalities: ['audio'].
Example fix
// before
const event = JSON.parse(data) as { choices?: ... }
// after — skip sentinels before parsing
if (data === '[DONE]')
break
const event = JSON.parse(data) as { choices?: ... } Defensive patterns
Strategy: try-catch
Validate before calling
function isSseDataLine(line: string): boolean {
return line.startsWith('data:') && !line.includes('[DONE]')
} Type guard
function isOpenRouterAudioChunk(event: unknown): event is { choices: Array<{ delta?: { audio?: { data?: string } } }> } {
return typeof event === 'object'
&& event !== null
&& Array.isArray((event as any).choices)
} Try / catch
for (const data of dataLines) {
if (data === '[DONE]') break
try {
const event = JSON.parse(data)
if (isOpenRouterAudioChunk(event) && event.choices[0]?.delta?.audio?.data)
chunks.push(event.choices[0].delta.audio.data)
}
catch {
// skip malformed chunk; count skips and warn if ratio is high
}
} Prevention
- Filter [DONE] and comment/keep-alive lines before JSON.parse.
- Run SSE routes through buffering-disabled proxies so chunks stay intact.
- Track a skipped-chunk counter to detect stream corruption early.
When it happens
Trigger: An SSE data line contains the '[DONE]' sentinel or a keep-alive/comment payload, the proxy mangles chunk boundaries (split multi-byte/bracketed JSON across lines), or the endpoint emits a non-JSON error frame inside the SSE stream.
Common situations: Streaming audio from an OpenRouter-compatible model through nginx/Cloudflare with buffering or fragmenting; model emits usage/final chunks with a different schema; trailing '[DONE]' line reaching the parser; upstream returning an HTML error page mid-stream.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- OpenRouter audio response has no body
- Streaming transcription response is missing a readable body.
- OpenRouter audio response has no body
- MiMo transcription failed: ${response.status} ${response.sta
- OpenRouter audio request failed: ${response.status} ${await
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/cf89933953e61caa.
Report an issue: GitHub.