Skip to content

Preserve hibernated MCP streams during cold restore - #1538

Open
jarell-cheong wants to merge 1 commit into
UsefulSoftwareCo:mainfrom
jarell-cheong:fix/cloudflare-mcp-cold-restore
Open

Preserve hibernated MCP streams during cold restore#1538
jarell-cheong wants to merge 1 commit into
UsefulSoftwareCo:mainfrom
jarell-cheong:fix/cloudflare-mcp-cold-restore

Conversation

@jarell-cheong

Copy link
Copy Markdown

Cloudflare MCP sessions can fail during a Durable Object cold restore with HTTP 500 and error 1101:

A call to blockConcurrencyWhile() in a Durable Object waited for too long. The call was canceled and the Durable Object was reset.

What happens

PartyServer restores hibernated WebSockets before its onStart hook rebuilds Executor's in-memory MCP runtime. startRuntimeFromOnStart always called closeRuntime, and closeRuntime always closed every active connection.

In a cold isolate, there is no previous runtime to replace. This closes the restored response stream that triggered startup while PartyServer is still inside its startup concurrency block.

In repeated fresh-session tests against a Cloudflare Workers deployment, the unpatched build failed 2 of 5 skills calls after approximately 30 seconds. Durable Object logs showed the request waiting until Cloudflare reset the object for the blockConcurrencyWhile timeout.

Fix

Detect whether the isolate has live in-memory runtime state before cleanup. A cold restore still releases stale in-memory resources, but it preserves hibernated response streams when there is no prior runtime. Warm restarts and failed-start cleanup continue to close streams.

Regression tests cover both paths:

  • a cold isolate preserves hibernated streams;
  • an in-memory restart still closes active streams.

Verification

  • bun run test src/mcp/agent-session-durable-object.test.ts in packages/hosts/cloudflare: 11/11 tests passed
  • bun run test in packages/hosts/cloudflare: 63/63 tests passed
  • bun run typecheck in packages/hosts/cloudflare
  • bun run typecheck in apps/host-cloudflare
  • bun run typecheck in apps/cloud
  • bunx vitest run --project cloudflare scenarios/browser-approval.test.ts in e2e: 2/2 tests passed
  • bun run format:check
  • bun run lint
  • bun run typecheck: 44/44 packages passed
  • patched Cloudflare Workers deployment: 5/5 fresh sessions passed, with restore completing in approximately 1.2 to 1.4 seconds

PartyServer can restore a Durable Object's WebSockets before onStart rebuilds Executor's in-memory MCP runtime. The existing cleanup path closed those restored response streams even though there was no prior runtime to replace, which could leave the startup block waiting on its own stream teardown until Cloudflare reset the object.

Only close active streams when onStart is replacing live in-memory runtime state. Keep the existing cleanup behavior for warm restarts and failed starts, and cover both lifecycle paths with regression tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant