Skip to content

Fix stale preview URLs - #841

Open
aron-cf wants to merge 2 commits into
mainfrom
stale-ports
Open

Fix stale preview URLs#841
aron-cf wants to merge 2 commits into
mainfrom
stale-ports

Conversation

@aron-cf

@aron-cf aron-cf commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Fixes #829: exposePort() succeeds but every request to the preview URL returns 410 STALE_PREVIEW_URL after a container restart — even while the container is healthy and serving traffic.

Root cause. Preview forwarding is gated on a runtime identity that rotates on every container start: onStart() calls currentRuntime.markStarted(), minting a fresh id. exposePort() writes a runtime-scoped activation stamped with the id current at that instant, and onStop() clears activations. Nothing re-derived the activation on start, so after any restart the proxy path saw runtime.owns(activation) === false and returned stale:

// validatePreviewURLForRuntime()
if (!runtime.owns(activation)) return { status: 'stale', reason: 'runtime-mismatch' };

Because exec/containerFetch starts the container (rotating the id via onStart), health-checking with exec actively re-invalidated the just-exposed port — which is why re-exposing appeared not to help and staleness persisted for minutes. Durable auth (portTokens) already survives restarts; only the runtime binding was missing.

Fix. Re-derive activations from the durable portTokens for the new runtime as part of onStart():

override async onStart() {
  const runtime = await this.currentRuntime.markStarted();
  await this.reactivateExposedPortsForRuntime(runtime); // new
  ...
}

private async reactivateExposedPortsForRuntime(runtime: RuntimeIdentity) {
  await this.ctx.storage.transaction(async (txn) => {
    const tokens = await this.readPortTokens(txn);
    const activations: PreviewPortActivations = {};
    for (const [port, entry] of Object.entries(tokens))
      activations[port] = runtime.scope({ token: entry.token });
    await this.writeActivePreviewPorts(activations, txn);
  });
}

This runs only for a container that is already starting, so it never wakes a stopped runtime, and it never resurrects unexposed/destroyed ports (those are absent from portTokens) — preserving the security intent of #708.


Open in Devin Review

@changeset-bot

changeset-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f3fd79b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@cloudflare/sandbox Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@aron-cf

aron-cf commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@ghostwriternr I've added a fix for the reported issue, however I do want to check that this is a bug, rather than the port code acting as intended...

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@pkg-pr-new

pkg-pr-new Bot commented Jul 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@cloudflare/sandbox@841

commit: f3fd79b

@github-actions

Copy link
Copy Markdown
Contributor

📦 Preview Build

Version: 0.0.0-pr-841-f3fd79bd

Install the SDK preview:

npm i https://pkg.pr.new/cloudflare/sandbox-sdk/@cloudflare/sandbox@841

🐳 Docker images were not rebuilt — no container changes detected. Use the latest release images from Docker Hub.

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.

STALE_PREVIEW_URL on every preview request despite healthy container (0.11.0)

1 participant