How to Fix 'streaming response cutoff during development' in LangGraph (TypeScript)
If you’re seeing streaming response cutoff during development in LangGraph TypeScript, it usually means the stream was terminated before the graph finished producing events. In practice, this shows up when you’re running a dev server, browser client, or proxy that buffers, times out, or disconnects before the full stream is consumed.
The error often looks like a partial AIMessageChunk stream, followed by a dropped connection or an incomplete event sequence from CompiledStateGraph.stream() or CompiledStateGraph.streamEvents().
The Most Common Cause
The #1 cause is using a streaming transport that gets cut off by the development runtime: Next.js route handlers, Vite dev proxying, serverless timeouts, or an HTTP client that closes the connection early.
In LangGraph TypeScript, the graph is usually fine. The problem is the delivery path.
Broken vs fixed pattern
| Broken pattern | Fixed pattern |
|---|---|
| Stream from a dev route that buffers or closes early | Use a proper streaming response with ReadableStream and keep the connection open |
| Consume only part of the stream on the client | Read until completion and handle disconnects explicitly |
// BROKEN: route handler returns a response too early
import { NextRequest } from "next/server";
import { graph } from "@/lib/graph";
export async function POST(req: NextRequest) {
const { messages } = await req.json();
const stream = await graph.stream(
{ messages },
{ streamMode: "messages" }
);
// This looks fine, but many dev setups buffer or terminate early.
return new Response(JSON.stringify({ ok: true }));
}
// FIXED: pipe LangGraph output through a real streaming response
import { NextRequest } from "next/server";
import { graph } from "@/lib/graph";
export async function POST(req: NextRequest) {
const { messages } = await req.json();
const encoder = new TextEncoder();
const readable = new ReadableStream({
async start(controller) {
try {
for await (const chunk of await graph.stream(
{ messages },
{ streamMode: "messages" }
)) {
controller.enqueue(
encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`)
);
}
} catch (err) {
controller.enqueue(
encoder.encode(`event: error\ndata: ${JSON.stringify(String(err))}\n\n`)
);
} finally {
controller.close();
}
},
});
return new Response(readable, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
},
});
}
If you’re using streamEvents(), the same rule applies. Don’t convert it into a normal JSON response halfway through the request lifecycle.
Other Possible Causes
1. Your dev server is timing out
Some dev servers and proxies kill long-lived requests after a short idle window.
// Example: serverless-style timeout risk
export const maxDuration = 5;
If your graph does tool calls, retrieval, or multi-step reasoning, five seconds is not enough.
2. You are not draining the stream on the client
If you create an EventSource, fetch body reader, or custom iterator and stop reading early, LangGraph will look like it cut off.
// BROKEN
const res = await fetch("/api/chat", { method: "POST" });
const reader = res.body?.getReader();
// read once and exit
await reader?.read();
// FIXED
const res = await fetch("/api/chat", { method: "POST" });
const reader = res.body?.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(decoder.decode(value));
}
3. A proxy or middleware is buffering SSE
Nginx, Vercel edge middleware patterns, local reverse proxies, and some auth layers buffer responses unless configured otherwise.
location /api/chat {
proxy_buffering off;
proxy_cache off;
}
For SSE-style output, buffering defeats streaming.
4. You are mixing incompatible stream modes
LangGraph TypeScript supports different stream modes like "values", "updates", "messages", and event-based APIs. If your client expects one shape and your server emits another, it can look like truncation.
// Server emits messages...
await graph.stream(input, { streamMode: "messages" });
// Client expects JSON object per chunk...
JSON.parse(chunk);
Match the consumer to the emitted format.
How to Debug It
- •
Run the graph without HTTP
- •Call
graph.invoke()directly in a Node script. - •If this works but streaming fails over HTTP, the issue is transport-related.
- •Call
- •
Switch to a minimal SSE endpoint
- •Remove auth middleware, logging middleware, and body parsers.
- •Keep only
ReadableStream,Content-Type: text/event-stream, and the LangGraph iterator.
- •
Log every chunk boundary
- •Print when iteration starts, each chunk arrives, and when
controller.close()runs. - •If you never hit the final log line, something upstream is aborting the request.
- •Print when iteration starts, each chunk arrives, and when
console.log("stream start");
for await (const chunk of await graph.stream(input)) {
console.log("chunk", chunk);
}
console.log("stream end");
- •Test in production mode locally
- •Build and run with
next build && next start. - •Many “development” cutoffs disappear in production because dev overlays and hot reload hooks are gone.
- •Build and run with
Prevention
- •Use real streaming responses for LangGraph outputs:
- •
ReadableStreamfor fetch-based clients - •SSE headers for browser consumers
- •
- •Keep long-running graphs away from dev-only timeouts:
- •raise route duration limits where supported
- •avoid heavy tool calls in local preview routes
- •Standardize one wire format per endpoint:
- •don’t mix raw chunks, JSON responses, and event streams on the same route
If you want a quick rule: if CompiledStateGraph.stream() works in Node but fails behind your app router or dev proxy, it’s almost never LangGraph itself. It’s usually buffering, timeout policy, or an incomplete client read loop.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit