ERR_STREAM_PREMATURE_CLOSE: Fix LLM Streaming API Errors
5 min fix · OpenAI · Anthropic · Groq · Node.js
ERR_STREAM_PREMATURE_CLOSE is a Node.js error thrown when a readable stream is destroyed before emitting 'end'. With LLM streaming APIs, it means the SSE connection dropped before the model finished generating. This guide covers why it happens and how to fix it.
Check provider status first
If this error just started or is happening to all users, the provider may be having a streaming outage. Check live status before changing code:
Common causes
- •HTTP client timeout too short — default timeouts (often 30–60s) expire before long completions finish streaming
- •Provider-side drop — streaming endpoints are more affected by incidents than REST calls; the provider closes the connection during degraded performance
- •Network/proxy timeout — NAT gateways, reverse proxies, or VPNs closing idle-looking SSE connections (they look idle because data arrives in bursts)
- •No error handler on the stream — in Node.js, an unhandled
'error'event throws and crashes the process
Step-by-step fix
Set an explicit timeout (120s+)
The most common fix. Long responses (thousands of tokens) take 60–120s to stream. The default HTTP timeout in Node.js is 0 (no timeout), but many HTTP clients and wrappers set shorter defaults.
// OpenAI SDK — pass timeout in the options object
const stream = await openai.chat.completions.create(
{ model: 'gpt-4o', messages, stream: true },
{ timeout: 120_000 } // 120 seconds
);
// Anthropic SDK — set in constructor
const anthropic = new Anthropic({ timeout: 120_000 });
// fetch with AbortController
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 120_000);
const res = await fetch(url, { signal: controller.signal });
clearTimeout(timeout); Add an error handler before iterating
Attach an 'error' listener before the loop. Without this, a premature close throws an unhandled error and crashes Node.js.
// OpenAI SDK
const stream = openai.chat.completions.stream({ model: 'gpt-4o', messages });
stream.on('error', (err) => {
console.error('Stream error:', err.message);
});
let partial = '';
try {
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content ?? '';
partial += delta;
process.stdout.write(delta);
}
} catch (err) {
if (err.code === 'ERR_STREAM_PREMATURE_CLOSE') {
console.warn('Stream closed early. Partial response:', partial);
// show partial to user or retry
} else {
throw err;
}
} Add retry with exponential backoff
ERR_STREAM_PREMATURE_CLOSE is often transient — a single retry succeeds. The OpenAI SDK does not auto-retry streaming errors; implement this yourself.
async function streamWithRetry(params, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const stream = await openai.chat.completions.create(
{ ...params, stream: true },
{ timeout: 120_000 }
);
let result = '';
for await (const chunk of stream) {
result += chunk.choices[0]?.delta?.content ?? '';
}
return result;
} catch (err) {
const isRetryable =
err.code === 'ERR_STREAM_PREMATURE_CLOSE' ||
err.status === 503 ||
err.status === 429;
if (isRetryable && attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000;
await new Promise(r => setTimeout(r, delay));
continue;
}
throw err;
}
}
} Fix reverse proxy / network timeouts
If you're behind nginx, Cloudflare, or AWS ALB, the proxy may cut SSE connections that look idle. Configure the proxy to allow long-lived connections:
# nginx — increase proxy read timeout for streaming routes
location /api/stream {
proxy_read_timeout 180s;
proxy_buffering off; # critical for SSE
proxy_cache off;
proxy_set_header Connection '';
proxy_http_version 1.1;
}
# Cloudflare — stream proxy timeout is 100s by default
# Route through Workers or set: cf.response_buffering = false FAQ
Is ERR_STREAM_PREMATURE_CLOSE my code or the provider?
If it happens consistently on every request, it's likely your code (missing timeout, no error handler). If it started suddenly or happens intermittently, check the provider status first — streaming endpoints are the first to fail during degraded performance. Prismix monitors this in real time at prismix.dev.
How do I fix it with the Anthropic SDK specifically?
Set timeout in the constructor: new Anthropic({ timeout: 120_000 }). For streams from anthropic.messages.stream(), catch errors with stream.on('error', handler) before the for-await loop. The Anthropic SDK wraps premature close as APIConnectionError.
Does this also affect browser fetch / EventSource?
Yes, but differently. In browsers, fetch stream errors surface as TypeError: Failed to fetch or the ReadableStream reader throws. EventSource auto-reconnects on disconnect but loses the partial response. For browser apps streaming LLM responses, accumulate chunks locally and handle the stream close event to show a "reconnecting" state.
Does Groq or Mistral have different behavior?
Groq's low-latency streaming generates very fast, so timeouts are rarely the issue — but the connection can still drop during incidents. Mistral's API has a default 30s server-side timeout on streaming. The same fixes apply: set explicit client timeout, add error handler, retry on premature close. Check live status at prismix.dev/service/groq.
Related guides