Skip to content

tunnels.get() is not idempotent in wrangler dev: second call throws 'Tunnel recovery attempts were exhausted' on a healthy tunnel and never recovers #874

Description

@zmarques

Versions

@cloudflare/sandbox 0.12.5, wrangler 4.123.0, wrangler dev --local (WSL2 + Docker Desktop)

Describe the bug

sandbox.tunnels.get(port) is not idempotent under wrangler dev --local: after the first successful call establishes a healthy quick tunnel, a second tunnels.get(port) for the same port immediately throws:

OperationInterruptedError: Tunnel recovery attempts were exhausted

The tunnel itself is still healthy at that moment (the *.trycloudflare.com URL keeps serving 200s), yet every subsequent tunnels.get on that Durable Object keeps failing with the same error. The exhausted state never resets on its own — only destroying and recreating the sandbox (stop → start) clears it.

The same happens after any transient failure (e.g. the target port briefly not listening): recovery attempts get consumed and the counter never resets even after the origin becomes healthy, so the DO is permanently unable to return a tunnel URL.

Expected behavior

tunnels.get(port) should be idempotent per port (return the existing healthy tunnel), and the recovery-attempt counter should reset after a successful connection — or at least be resettable without recycling the sandbox.

Repro

const sandbox = getSandbox(env.Sandbox, 'repro')
await sandbox.startProcess('python3 -m http.server 8080', { processId: 'srv' })
const a = await sandbox.tunnels.get(8080) // ok, URL works
const b = await sandbox.tunnels.get(8080) // throws OperationInterruptedError

Workaround

Cache the tunnel URL out-of-band (we write it to a file in the container) and only call tunnels.get when the dev-server process is (re)started; never call it twice per container lifetime.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions