Summary
sandbox.exposePort() succeeds (its assertActive(runtime) passes), but every request to the returned preview URL returns 410 STALE_PREVIEW_URL ("Preview URL is stale because the sandbox runtime is not active") — even while the same sandbox's container is verifiably healthy and serving traffic.
Environment
@cloudflare/sandbox 0.11.0 (worker-side and DO)
- Container image:
cloudflare/sandbox:0.11.0-desktop
- Worker deployed with a wildcard route (
*.example.com/*) + run_worker_first: true; proxyToSandbox middleware intercepts preview hostnames
- Sandbox accessed via
getSandbox(env.Sandbox, taskId) (also tried { keepAlive: true })
Repro
- Custom domain worker; wildcard DNS exists and TLS is valid (one level under the zone apex).
const sandbox = getSandbox(env.Sandbox, id);
const { url } = await sandbox.exposePort(6080, { hostname: "example.com" }) → returns https://6080-<id>-<token>.example.com/ (HTTP 200).
GET <url>vnc.html → 410 {"error":"Preview URL is stale because the sandbox runtime is not active","code":"STALE_PREVIEW_URL"}.
The stale response persists for 4+ minutes of polling and across repeated exposePort() re-registrations (same token returned each time). keepAlive: true does not change it.
Expected
Preview URL forwards to the container port once the container is running.
Notes
- The same sandbox executes commands fine via
exec (container boots, agent responds), so the runtime is active in practice.
- From reading the DO code,
fetchPreviewIfRunning returns stale when !container?.running || state.status !== "healthy" or !currentRuntime.isActive(runtime). Since exposePort()'s assertActive(runtime) passes, the registration-side state disagrees with the proxy-side state somewhere (possibly a runtime-id mismatch after DO migration/restart, or the health gate never reaching healthy on the desktop image).
- Same behavior with the SDK's
getDesktopStreamUrl(hostname) convenience on the desktop image.
- Happy to provide a minimal repro repo if useful.
Summary
sandbox.exposePort()succeeds (itsassertActive(runtime)passes), but every request to the returned preview URL returns410 STALE_PREVIEW_URL("Preview URL is stale because the sandbox runtime is not active") — even while the same sandbox's container is verifiably healthy and serving traffic.Environment
@cloudflare/sandbox0.11.0 (worker-side and DO)cloudflare/sandbox:0.11.0-desktop*.example.com/*) +run_worker_first: true;proxyToSandboxmiddleware intercepts preview hostnamesgetSandbox(env.Sandbox, taskId)(also tried{ keepAlive: true })Repro
const sandbox = getSandbox(env.Sandbox, id);const { url } = await sandbox.exposePort(6080, { hostname: "example.com" })→ returnshttps://6080-<id>-<token>.example.com/(HTTP 200).GET <url>vnc.html→410 {"error":"Preview URL is stale because the sandbox runtime is not active","code":"STALE_PREVIEW_URL"}.The stale response persists for 4+ minutes of polling and across repeated
exposePort()re-registrations (same token returned each time).keepAlive: truedoes not change it.Expected
Preview URL forwards to the container port once the container is running.
Notes
exec(container boots, agent responds), so the runtime is active in practice.fetchPreviewIfRunningreturns stale when!container?.running || state.status !== "healthy"or!currentRuntime.isActive(runtime). SinceexposePort()'sassertActive(runtime)passes, the registration-side state disagrees with the proxy-side state somewhere (possibly a runtime-id mismatch after DO migration/restart, or the health gate never reachinghealthyon the desktop image).getDesktopStreamUrl(hostname)convenience on the desktop image.