cloudflare / cloudflare/workers-sdk
Cloudflare-Workers-Version-Overrides is ignored on fetch Service Binding when targeting a 0% version in the current deployment
- Dominant language
- TypeScript
- Stars
- 4.5k
- Forks
- 1.5k
- Avg merge
- 3d 8h
- Merged PRs (30d)
- 186
Description
# Cloudflare-Workers-Version-Overrides is ignored on fetch Service Binding when targeting a 0% version in the current deployment
## Summary
A production-deployed caller Worker made a `fetch()`-based Service Binding subrequest to a downstream Worker whose current deployment contained two versions: version A at 100% traffic and version B at 0% traffic. Setting `Cloudflare-Workers-Version-Overrides` to version B did not change the downstream behavior. Both the no-override and override requests executed version A behavior.
Classification: `PRODUCTION_SERVICE_BINDING_OVERRIDE_NOT_APPLIED`.
## Documented expectation
The current [Cloudflare Workers version overrides documentation](https://developers.cloudflare.com/workers/versions-and-deployments/version-overrides/) states that an override can target a version in the current deployment, including a version serving 0% of traffic. It also states that `Cloudflare-Workers-Version-Overrides` can be set on a `fetch()`-based Service Binding subrequest, in which case the downstream Worker should execute the specified version.
## Environment
- Wrangler `4.129.0`
- Module Workers
- Compatibility date: `2026-09-04`
- Caller and downstream Worker in the same Cloudflare account
- Downstream active deployment contained exactly two versions:
- version A: 100% traffic
- version B: 0% traffic
- The target version B was part of the **current deployment**, not merely an uploaded preview version.
- Service Binding used `fetch()`, not RPC.
- No custom domain was required.
## Minimal reproduction shape
Use two Workers:
`downstream-worker`
- Version A exposes exactly three observable MCP tools.
- Version B exposes exactly four observable MCP tools.
- Its active deployment is A = 100% and B = 0%.
`caller-worker`
- Has exactly one Service Binding: `DOWNSTREAM` to `downstream-worker`.
- Builds the same outgoing request in both tests.
- Test A sends no version override.
- Test B adds only the version-override header.
```ts
const headers = new Headers(baseHeaders);
if (useOverride) {
headers.set(
"Cloudflare-Workers-Version-Overrides",
'downstream-worker=""',
);
}
return env.DOWNSTREAM.fetch(
new Request("https://downstream.example/mcp", {
method: request.method,
headers,
body,
}),
);
```
The reproduction also set an explicit canonical `Host` header derived from the downstream request URL because the downstream MCP handler validates `Host`. That resolved an earlier authenticated `403` and is separate from the version-override result: both Test A and Test B used the same canonical Host header.
## Observed production result
The caller was deployed as a normal Worker with a temporary `workers.dev` endpoint. It was not run with `wrangler dev --remote`, edge preview, or a temporary-deployment command.
Before authentication, the caller-to-downstream path returned the expected `401` with the downstream OAuth challenge. Canonical OAuth authorization, callback, and allowlist validation then passed.
### Test A — no override
- Authenticated downstream MCP initialization: PASS
- Observed version A behavior
- Exactly three tools: `render_bridge_canary`, `canary_ping`, `model_pipeline_probe`
### Test B — override targeting version B
- Authenticated downstream MCP initialization: PASS
- Still observed version A behavior
- Exactly the same three tools: `render_bridge_canary`, `canary_ping`, `model_pipeline_probe`
- No error indicated that the override header was malformed.
- The candidate version remained at 0% in the downstream current deployment.
The same `OVERRIDE_NOT_APPLIED` behavior was observed earlier from `wrangler dev --remote` / edge preview. The important result here is that the behavior also reproduces from a normally deployed production-runtime caller.
## Safety and control evidence
- Version B was confirmed to be in the downstream current deployment at 0%.
- Baseline and override requests used the same Service Binding, canonical request URL, canonical Host header, authenticated request path, and MCP verifier.
- Authenticated initialization succeeded in both tests.
- The only semantic request difference was `Cloudflare-Workers-Version-Overrides`.
- Downstream application state was unchanged during the reproduction.
- The disposable production caller was deleted immediately after testing.
## Expected result
Test B should execute version B and expose four-tool behavior.
## Actual result
Test B executed the same 100%-traffic version A behavior as Test A and exposed the same three tools.
## Related context
This may relate to [cloudflare/workers-sdk#9342](https://github.com/cloudflare/workers-sdk/issues/9342). This reproduction is narrower: it concerns the currently documented ability to override a version that is already in the active deployment at 0%, not arbitrary uploaded preview versions.
## Questions for Cloudflare
1. Is fetch-based Service Binding version override currently supported in production for a target version at 0%?
2. If yes, is this a platform bug?
3. Is there an undocumented restriction involving `workers.dev`, OAuth-protected downstream Workers, or manually constructed `Request` objects?
4. Is there a supported way to observe whether the routing layer accepted or rejected a version override?
Contributor guide
Research direction
The issue names no repository file or test; start by reading the linked version-overrides documentation and locating the production fetch-based Service Binding and version-override handling in workers-sdk. Reproduce the two-version deployment with the supplied caller and downstream Worker shape. Done means a 0% version in the current deployment is selected by the override, while the no-override request still uses the 100% version.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend, cloud
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100