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.
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'scloudflare_worker_secretresources), those versions become the newest inlistWorkerVersions.updateDeploymentMappingthen 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_secretresources withdepends_on = [null_resource.opennext_deploy]. After an initial deploy we get:On the next deploy,
getDeploymentMappingcapturesV5.idfor the previous deploy. Requests with the previous deploy'sdplhit the skew protection redirect: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]!.idreplaces"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 inupdateDeploymentMapping) to only consider versions that carry the asset bundle. Theannotations.workers/triggered_byfield on each version distinguishes them:upload/version_upload-> fromwrangler deploy/wrangler versions upload, has assetssecret-> from secret API updates, no assetsEither 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.