cloudflare / cloudflare/sandbox-sdk

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

Open
#874 0 comments 0 reactions 1 assignee Claimed by @ghostwriternr View on GitHub
bug
Dominant language
TypeScript
Stars
1.1k
Forks
114
Avg merge
22h 42m
Merged PRs (30d)
14

Description

### 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

```ts
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.

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.