cloudflare / cloudflare/workers-sdk

Durable Object RPC ReadableStream cancel logs “Network connection lost” and later “IoContext”

Open
#11,071 2 comments 0 reactions 1 assignee Claimed by @lrapoport-cf View on GitHub
package:wrangler
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.