Skip to content

Skew protection can map previous deployments to asset-less worker versions #1230

@alex-all3dp

Description

@alex-all3dp

Skew protection can map previous deployments to asset-less worker versions

When a deploy pipeline creates new worker versions after wrangler deploy (e.g. Terraform's cloudflare_worker_secret resources), those versions become the newest in listWorkerVersions. updateDeploymentMapping then picks them to replace the previous "current" sentinel, which means old ?dpl=... requests get redirected to a preview URL that doesn't serve static assets.

Reproduction

Terraform config that creates cloudflare_worker_secret resources with depends_on = [null_resource.opennext_deploy]. After an initial deploy we get:

V1 wrangler upload     <- has assets
V2 terraform secret    <- no assets
V3 terraform secret
V4 terraform secret
V5 terraform secret

On the next deploy, getDeploymentMapping captures V5.id for the previous deploy. Requests with the previous deploy's dpl hit the skew protection redirect:

$ curl -sI "https://<V5-id>-<worker>.<preview-domain>.workers.dev/_next/static/css/<hash>.css"
HTTP/2 404
x-preview-user-error: true

The wrangler-triggered versions in the same cluster (V1 here) serve the asset fine (200). The redirect just lands on the wrong one.

Root cause

In skew-protection.ts#L148, versions[0]!.id replaces "current" with whichever worker version was created most recently, regardless of what triggered it. Secret API calls, binding changes, and similar metadata-only operations all create new versions without re-uploading the asset bundle.

Proposed fix

Filter listWorkerVersions (or the replacement in updateDeploymentMapping) to only consider versions that carry the asset bundle. The annotations.workers/triggered_by field on each version distinguishes them:

  • upload / version_upload -> from wrangler deploy / wrangler versions upload, has assets
  • secret -> from secret API updates, no assets
  • other metadata-only triggers likewise skip the asset bundle

Either drop non-upload triggers explicitly, or keep an allowlist of upload-type triggers. I'd lean allowlist since it fails safe against future trigger types.

Happy to open a PR if the approach makes sense.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions