workerd: io context cancelled, streaming response failed mid-body
Fixes 'io context cancelled' when a workerd streaming response dies mid-body. Use it when streams fail after the client disconnects or the handler settles early. The key trigger is the cancelled I/O context message; the fix is tying the stream to the request lifetime and using waitUntil for trailing work.
TL;DR
Workerd cancels the request's I/O context when the fetch handler settles or the client goes away, and any stream still writing at that point dies mid-body. Tie the stream's lifetime to the request (listen for the abort signal and close up cleanly) and move anything that must run after the response into ctx.waitUntil, which keeps its own context alive.
Error: I/O context cancelledSteps
- Reproduce it: stream a slow response from
wrangler devand cancel the client mid-download.
Expected: the dev log shows the I/O context cancelled error at the moment the client disconnects.
- Hook the request's abort signal into your stream so a disconnect ends the stream cleanly instead of mid-write:
const stream = new ReadableStream({
start: function (controller) {
request.signal.addEventListener("abort", function () {
controller.close();
});
}
});Expected: client disconnects close the stream; no more mid-body exceptions.
- Move any work that must happen after the response (logging, metric flushes, cleanup writes) out of the streaming path and into waitUntil:
ctx.waitUntil(logStreamEnd(request));Expected: trailing work runs in a context that survives the response finishing.
- Do not hold the handler open just to keep the stream alive; return the streaming response promptly and let the stream drive itself.
Expected: the handler returns, the stream flows, and a disconnect is handled by step 2.
- Redeploy and re-run the cancel-mid-download test against the deployed worker.
Expected: disconnects are clean and the error no longer appears.
Use this when
- Workerd logs "I/O context cancelled" during a streaming response
- Server-sent event or chunked streams die when the client disconnects
- The error appears after the handler returned but the stream was still open
Not for this skill when
- The stream never starts; that is a construction bug, not a cancellation
- The worker is killed by the CPU time limit mid-stream; that is a quota error
- The client receives the full body fine and the error is only in logs for abandoned downloads
Variant phrasings
- workerd i/o context was cancelled streaming
- cloudflare worker stream fails when client disconnects
- "The I/O context was cancelled" readable stream worker
Why it happens
Every request in workerd gets an I/O context that lives exactly as long as the request needs it. When the handler settles or the client disconnects, the runtime reclaims that context; writes to a stream anchored to it then throw. Code written for servers where streams outlive the handler hits this the moment a slow consumer or an early return is involved.
Edge cases
- Infinite streams (live feeds) must handle disconnects or every dropped client logs an error; the abort hook is mandatory there, not optional.
- waitUntil extends background work but does not extend the response stream itself; the stream still ends when the client goes away.
- A proxy or CDN between the client and the worker can hold the connection open after the real client left; test disconnects end to end.
- Closing the controller on abort while a write is in flight can still race; guard the write loop with a cancelled flag checked between chunks.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst8do1VrmE8P7OEWLIPxUWw
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.