Skip to content

[BUG] wrap claude + Foundry: proxy 404s — /anthropic/v1/messages has no route, falls through to openai_passthrough #1874

Description

@euphoria95

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

  1. 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>
  1. claude (no wrap) → works. Banner: Sonnet 5 · Microsoft Foundry. Requests succeed.
  2. 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)

  1. Register the Anthropic handler for /anthropic/v1/messages too, stripping the /anthropic prefix before dispatch.
  2. Teach the catch-all passthrough to recognize the Foundry /anthropic prefix and dispatch to handle_anthropic_messages.
  3. Don't append /anthropic in _foundry_proxy_url(); append it instead in the Anthropic Foundry forwarder when building the real upstream URL.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions