Environment
- headroom: 0.30.0
- Claude Code: v2.1.204
- OS: Linux (x64), Node v26.3.0
- Upstream: real Azure AI Foundry —
ANTHROPIC_FOUNDRY_BASE_URL=https://<resource>.services.ai.azure.com/anthropic
- Auth:
ANTHROPIC_FOUNDRY_API_KEY (API key, not Entra)
Summary
headroom wrap claude against Azure AI Foundry fails every request with HTTP 404. Claude Code surfaces this as The model claude-sonnet-5 is not available on your foundry deployment, but the model is deployed and reachable: running claude directly (no wrap) against the same deployment works. The 404 comes from the proxy misrouting the request — the Anthropic handler is never invoked.
Steps to reproduce
- Configure Claude Code for Microsoft Foundry:
export CLAUDE_CODE_USE_FOUNDRY=1
export ANTHROPIC_FOUNDRY_BASE_URL=https://<resource>.services.ai.azure.com/anthropic
export ANTHROPIC_FOUNDRY_API_KEY=<key>
claude (no wrap) → works. Banner: Sonnet 5 · Microsoft Foundry. Requests succeed.
headroom wrap claude → every request returns 404; CC reports the model as unavailable.
Expected
The wrapped request reaches handle_anthropic_messages, is compressed, and is forwarded to the Foundry /anthropic upstream — same result as the unwrapped path.
Actual
Every request 404s before reaching the Anthropic handler.
Root cause
In Foundry mode, _foundry_proxy_url() (headroom/cli/wrap.py) sets the child's ANTHROPIC_FOUNDRY_BASE_URL to http://127.0.0.1:<port>/anthropic — it appends /anthropic to mirror the real Foundry URL shape. Claude Code's SDK then appends /v1/messages, so the proxy receives:
POST /anthropic/v1/messages
But register_provider_routes() (headroom/providers/proxy_routes.py, ~L485) registers the Anthropic handler only for the bare path:
@app.post("/v1/messages")
async def anthropic_messages(request: Request):
return await proxy.handle_anthropic_messages(request)
There is no route for /anthropic/v1/messages. The request falls through to the catch-all passthrough, which selects forwarder=openai_passthrough and returns 404. handle_anthropic_messages (compression, Foundry forwarding) is never called.
Evidence (proxy.log, sanitized)
event=proxy_inbound_request path=/anthropic/v1/messages query=beta=true host=127.0.0.1:8787
event=outbound_headers forwarder=openai_passthrough stripped_count=1
event=proxy_inbound_response path=/anthropic/v1/messages status=404
The session summary reports Total requests: 0, consistent with the Anthropic handler being bypassed.
Related
Suggested fix (any one)
- Register the Anthropic handler for
/anthropic/v1/messages too, stripping the /anthropic prefix before dispatch.
- Teach the catch-all passthrough to recognize the Foundry
/anthropic prefix and dispatch to handle_anthropic_messages.
- Don't append
/anthropic in _foundry_proxy_url(); append it instead in the Anthropic Foundry forwarder when building the real upstream URL.
Environment
ANTHROPIC_FOUNDRY_BASE_URL=https://<resource>.services.ai.azure.com/anthropicANTHROPIC_FOUNDRY_API_KEY(API key, not Entra)Summary
headroom wrap claudeagainst Azure AI Foundry fails every request with HTTP 404. Claude Code surfaces this asThe model claude-sonnet-5 is not available on your foundry deployment, but the model is deployed and reachable: runningclaudedirectly (no wrap) against the same deployment works. The 404 comes from the proxy misrouting the request — the Anthropic handler is never invoked.Steps to reproduce
claude(no wrap) → works. Banner:Sonnet 5 · Microsoft Foundry. Requests succeed.headroom wrap claude→ every request returns 404; CC reports the model as unavailable.Expected
The wrapped request reaches
handle_anthropic_messages, is compressed, and is forwarded to the Foundry/anthropicupstream — same result as the unwrapped path.Actual
Every request 404s before reaching the Anthropic handler.
Root cause
In Foundry mode,
_foundry_proxy_url()(headroom/cli/wrap.py) sets the child'sANTHROPIC_FOUNDRY_BASE_URLtohttp://127.0.0.1:<port>/anthropic— it appends/anthropicto mirror the real Foundry URL shape. Claude Code's SDK then appends/v1/messages, so the proxy receives:But
register_provider_routes()(headroom/providers/proxy_routes.py, ~L485) registers the Anthropic handler only for the bare path:There is no route for
/anthropic/v1/messages. The request falls through to the catch-all passthrough, which selectsforwarder=openai_passthroughand returns 404.handle_anthropic_messages(compression, Foundry forwarding) is never called.Evidence (proxy.log, sanitized)
The session summary reports
Total requests: 0, consistent with the Anthropic handler being bypassed.Related
wrap+CLAUDE_CODE_USE_FOUNDRY), different failure mode. That report fails earlier, in Claude Code's auth layer (azureADTokenProvider: ChainedTokenCredential authentication failed), with noANTHROPIC_FOUNDRY_API_KEYand a non-Azure (LiteLLM/Bedrock) upstream. Here auth succeeds, so the request gets past that point and hits the routing 404.ANTHROPIC_FOUNDRY_BASE_URL=http://127.0.0.1:8787(no/anthropicsuffix), so this path-miss could not occur there. The/anthropicsuffix — and thus this bug — was introduced later, around the proxy: support CLAUDE_CODE_USE_FOUNDRY and custom upstream gateways #726 / feat(azure-foundry): derive upstream URL from ANTHROPIC_FOUNDRY_RESOURCE #1138 Foundry work.Suggested fix (any one)
/anthropic/v1/messagestoo, stripping the/anthropicprefix before dispatch./anthropicprefix and dispatch tohandle_anthropic_messages./anthropicin_foundry_proxy_url(); append it instead in the Anthropic Foundry forwarder when building the real upstream URL.