Skip to content
gentic.news — AI News Intelligence Platform
Connecting to the Living Graph…

Listen to today's AI briefing

Daily podcast — 5 min, AI-narrated summary of top stories

Terminal window showing Claude Code streaming logs with a highlighted SSE middleware config block in a code editor…
Open SourceScore: 66

Fix Claude Code Streaming Stalls with claude-code-router's New SSE

Deploy claude-code-router's SSE coalescing middleware via NODE_OPTIONS --require to merge per-token deltas, cutting events 8x and eliminating hour-long stalls in Claude Code's UI.

·1d ago·4 min read··25 views·AI-Generated·Report error
Share:
Source: dev.tovia devto_claudecode, gh_claude_releases, simon_willison, medium_claudeMulti-Source
How do I fix Claude Code streaming stalls caused by per-token SSE events?

Use claude-code-router's SSE coalescing middleware to merge consecutive content_block_delta events into chunks, set CCR_SSE_COALESCE_MS (e.g., 40) to control window size, and load it via NODE_OPTIONS="--require /path/to/sse-coalesce.cjs" so every node process runs it.

TL;DR

Merge SSE deltas into chunks to prevent UI stalls, and load your middleware via NODE_OPTIONS --require, not patches.

What Changed — The SSE Coalescing Middleware in claude-code-router

If you run Claude Code through a self-hosted proxy like claude-code-router (ccr), you might have hit a nasty stall: approval prompts sitting for an hour because the UI extension couldn't keep up with per-token SSE events. The fix is a new middleware that merges those events into chunks, and it's now available in ccr.

The middleware, sse-coalesce.cjs, sits between the model provider and the Claude Code CLI. It re-chunks content_block_delta events so the stream looks like what the official Anthropic API already sends—merged chunks that the VS Code extension digests without choking.

What It Means For You

If you're using a third-party provider (DeepSeek, Zhipu, etc.) through ccr, you're exposed to the same problem: providers emit one event per token, and the extension's per-event rendering cost (estimated at 250-400ms) turns a 200-token response into 85 events that take minutes to display. This middleware collapses those events, cutting a 200-token response from 85 events to 11—an 8x reduction.

The middleware is safe by design: it only merges events that are provably interchangeable—same block index, same delta type (text into text, thinking into thinking, partial JSON into partial JSON). It flushes the moment any other event appears (like content_block_start or message_stop), so protocol semantics are preserved. It also handles transport realities: removes stale content-length headers, bypasses itself if compression isn't identity, and respects backpressure from the downstream consumer.

Try It Now — Setup and Configuration

  1. Get the middleware file: Place sse-coalesce.cjs in a directory you control (e.g., /data/.claude-code-router/). Keep it in your ops repo and bind-mount it read-only into the container.

  2. Load it into every node process: Add this to your ccr container's environment:

    NODE_OPTIONS="--require /data/.claude-code-router/sse-coalesce.cjs"
    

    This ensures every node process (core server and gateway) loads the module at birth. The module must self-install at load time—don't just export an install() function that never gets called.

  3. Configure the window: Set CCR_SSE_COALESCE_MS (e.g., 40) to control how long the middleware waits before merging. Per-type overrides exist: CCR_SSE_COALESCE_THINKING_MS, _TEXT_MS, _INPUT_JSON_MS. A value of zero or less falls back to the global—not "off". Set CCR_SSE_DROP_PINGS=0 to keep keep-alive pings (but dropping them is better to avoid flushing mid-thinking).

  4. Deploy changes: After editing the middleware, run docker compose up -d --force-recreate—bind mounts don't trigger reloads, and --require only loads at process birth.

  5. Verify: Check the middleware's stats log (truncated at 256 KB) to see merge counts. Example: a 200-token response went from 85 events to 11.

Why This Works

The root cause was a slow reader (the extension) backing up a pipe. The middleware smooths the stream's shape without changing its meaning. It's a targeted fix that respects the protocol—unlike naive buffering that could balloon memory. The official Anthropic API already streams in chunks, so this makes third-party providers behave the same way.

Caveats and Lessons

  • Don't patch globalThis.fetch: ccr's gateway uses undici's dispatcher, not fetch. Patch the API actually in use.
  • Don't edit generated files: The core server overwrites the gateway's preload file on startup. Bring your own file at a path the core doesn't know.
  • Backpressure matters: The middleware pauses when the consumer signals stop, preventing a balloon effect.
  • Tune the window: A 40ms window buys only 3-5x reduction if the provider emits every 25-50ms. Adjust based on your stream's tempo.

This fix turned an hour-long stall into a three-minute display delay—a massive improvement. For most users, the default settings work; start with CCR_SSE_COALESCE_MS=40 and adjust based on your provider's streaming pattern.


Source: dev.to

[Updated 16 Aug via devto_claudecode]

The middleware’s rollout was harder than the design. [per dev.to] The author needed five attempts to get it running: patching globalThis.fetch failed because ccr's gateway uses undici's dispatcher, not fetch. Moving to the dispatcher layer worked but only in the gateway process—the core server, which handles some provider traffic (e.g., DeepSeek), remained unpatched. The fix required loading the middleware into every node process via NODE_OPTIONS="--require", ensuring both core and gateway load it at birth. This multi-process insight is critical for anyone deploying ccr in containers with separate processes.

Source: gentic.news · · author= · citation.json

AI-assisted reporting. Generated by gentic.news from multiple verified sources, fact-checked against the Living Graph of 4,300+ entities. Edited by Ala SMITH.

Following this story?

Get a weekly digest with AI predictions, trends, and analysis — free.

AI Analysis

Claude Code users who route through claude-code-router should immediately enable this middleware. The key action is to add `NODE_OPTIONS="--require /path/to/sse-coalesce.cjs"` to your ccr container, ensuring the module self-installs on load. This eliminates the hour-long approval stalls that occur when using third-party providers with per-token SSE streams. Set `CCR_SSE_COALESCE_MS` to 40 initially, then tune based on your provider's emission rate—check the stats log to see how many events you're merging. Beyond the middleware, the article's debugging journey offers a checklist for any Claude Code proxy customization: patch the actual API layer (undici dispatcher, not fetch), deploy across the entire process tree (NODE_OPTIONS), avoid generated artifacts, and remember that changed files don't reload without container recreation. These lessons apply to any custom middleware or hooks you're building for Claude Code. Finally, note that the article also mentions a related hygiene fix: ccr's request log was capturing full bodies (156 MB), which was reduced to errors-only (12 MB). If you're running ccr, audit your logging to avoid similar bloat that could impact performance.
This story is part of
The AI Infrastructure War Shifts from Chips to Developer Tools
Nvidia's enterprise pivot and AWS's OpenAI bet collide with Cursor's quiet ascent
Compare side-by-side
Anthropic vs DeepSeek
Enjoyed this article?
Share:

AI Toolslive

Five one-click lenses on this article. Cached for 24h.

Pick a tool above to generate an instant lens on this article.

Related Articles

From the lab

The framework underneath this story

Every article on this site sits on top of one engine and one framework — both built by the lab.

More in Open Source

View all