cloudflare / cloudflare/workers-sdk
Durable Object RPC ReadableStream cancel logs “Network connection lost” and later “IoContext”
- Dominant language
- TypeScript
- Stars
- 4.5k
- Forks
- 1.5k
- Avg merge
- 3d 8h
- Merged PRs (30d)
- 186
Description
Durable Object RPC ReadableStream cancel logs “Network connection lost” and later “IoContext”
Summary
- Canceling a ReadableStream returned from a Durable Object (via DO RPC) and proxied by a Worker results in Wrangler dev logs showing “Network connection lost” almost immediately, followed later (~1 minute) by an “IoContext” message — despite the stream’s cancel() being invoked in both the Worker and the DO.
Environment
- Wrangler: 4.44.0 (devDependency in repro)
- Node: v24.10.0
- OS: macOS Darwin 15.6
- Bun: 1.3.0 (for completeness)
Minimal reproduction
- Repo: https://github.com/IMax153/do-stream-cancel-repro
- Setup:
- `pnpm install`
- `pnpm dev`
- In a second terminal:
- `curl -X POST localhost:8787/rpc`
- After a few seconds, Ctrl+C to cancel the response stream
Expected
- Canceling the proxied ReadableStream cleanly tears down the stream without transport errors.
- DO source `cancel()` is called and shutdown is graceful, no “Network connection lost” or delayed “IoContext” error logs.
Actual
- “Network connection lost” appears quickly after Ctrl+C on the client.
- After ~1 minute, an “IoContext” error line appears.
- Screenshot in repro: assets/images/error.png
- Raw URL: https://raw.githubusercontent.com/IMax153/do-stream-cancel-repro/refs/heads/main/assets/images/error.png
Relevant code (src/index.ts)
- DO returns a stream and logs on cancel:
```ts
return new ReadableStream({
async start(controller) {
for await (const value of self.dataSource) {
controller.enqueue(new TextEncoder().encode(String(value)))
}
},
cancel() {
console.log('CANCELLED')
},
})
```
- Worker forwards the stream and cancels on client abort:
```ts
const stream = await stub.rpc()
request.signal.addEventListener('abort', () => {
console.log('CLIENT ABORTED REQUEST')
stream.cancel()
})
return new Response(stream, { headers: { 'Content-Type': 'application/octet-stream' } })
```
Configuration (wrangler.jsonc)
```jsonc
{
"compatibility_date": "2025-10-22",
"compatibility_flags": [
"enable_request_signal",
"nodejs_compat"
],
"durable_objects": {
"bindings": [
{ "class_name": "MyDurableObject", "name": "MY_DURABLE_OBJECT" }
]
}
}
```
Notes
- The DO’s `cancel()` reliably logs (“CANCELLED”), confirming cancellation propagates across the DO RPC boundary.
- If these logs are expected at a lower level (e.g. transport closing) it’d be helpful to clarify whether this should be surfaced as an error or as a benign cancellation signal; as-is, it looks like an actual network fault during normal cancellation.
- Happy to run additional diagnostics (e.g. specific logging flags) if it helps pinpoint whether this is a Miniflare/workerd/wrangler presentation issue vs. runtime behavior.
Thanks!
Contributor guide
Assessment
This issue has not been assessed yet.