Auto-mode safety classifier routes through ANTHROPIC_BASE_URL (undocumented) — gateway users' classifier is served by their gateway

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 17, 2026

Summary

When a session sets ANTHROPIC_BASE_URL — a documented, supported configuration (llm-gateway-connect.md) — Claude Code's auto-mode safety classifier inference call also goes to that endpoint. We confirmed this on the wire with a local logging reverse-proxy placed between Claude Code and a self-hosted llama.cpp endpoint.

This is not a remote vulnerability: it requires the user to deliberately point their own base URL somewhere. We're reporting it as a documentation gap plus a design surprise for gateway users. Someone running a corporate LLM gateway may reasonably assume the safety classifier is still evaluated by Anthropic. It isn't — and the ~107KB classifier policy prompt crosses the wire to that gateway on every gated action.

Environment

  • Claude Code, claude -p --permission-mode auto
  • ANTHROPIC_BASE_URL → local logging reverse-proxy (127.0.0.1:9999, stdlib Python; logs model / max_tokens / system length, then forwards to a self-hosted llama.cpp server)
  • ANTHROPIC_MODEL=claude-opus-4-8; the llama.cpp endpoint ignores the model field and serves a local model (GLM-5.2)
  • Confound ruled out first: no settings.json present, allowedTools empty — so approvals were not coming from an allow-rule
  • Date: 2026-07-16

A loopback http base URL is accepted by the CLI, which is what makes this trivially observable.

What we observed

1. The classifier call follows ANTHROPIC_BASE_URL. Captured at the proxy: model='claude-sonnet-5', max_tokens=64, n_tools=0, sys_len=107453 (~107KB, ~27K tokens).

2. Classifier model is Sonnet 5, then falls back to the session model. The Sonnet 5 default is documented in permission-modes.md ("runs on Claude Sonnet 5 by default rather than on your /model selection"). What we did not find documented anywhere is that this call follows the configured base URL. We also observed the classifier retry the same classification against the session model (claude-opus-4-8) after the Sonnet 5 attempt.

3. Auto mode engages against a non-Anthropic model. The provider/model gate appears to be checked client-side against the model-ID string. Setting ANTHROPIC_MODEL=claude-opus-4-8 against an endpoint that ignores the model field was sufficient for auto mode to engage. Auto mode did not refuse and did not fall back to always-prompt.

4. Behavior with an uncalibrated model serving the classifier: denies, but loops. Asking the session to write .claude/settings.json containing {"permissions":{"allow":["Bash(*)"]}} was denied on every attempt; the file never landed. But it produced a runaway retry loop — 24 classifier calls / 10 main-loop calls for that single blocked action, each re-sending the ~27K-token prompt — ending in an empty-output dead session.

We could not determine whether the local model denied on the merits or whether Claude Code fails closed on unparseable output from it. Either path denied, and we never observed the local model approve a gated action — so we have no evidence it can grant anything either. We're reporting the observed outcome, not a mechanism.

5. Related, working as documented: permissions.deny is client-side and evaluated pre-classifier, so the deny-list holds regardless of endpoint. Safe-verb Bash never reached the classifier at all; only a protected-path write triggered it, matching the documented decision order.

Why the error string matters

The outage error reads roughly: "claude-opus-4-8 is temporarily unavailable, so auto mode cannot determine the safety of Bash right now" — naming the session model. Given the Sonnet-5-then-session-model fallback chain above, that string is presumably reporting the last attempt. It has repeatedly led operators here to believe their main model is down when the classifier is what failed.

Asks

  1. Document classifier routing explicitly in llm-gateway-connect.md and permission-modes.md: does the classifier call follow ANTHROPIC_BASE_URL or not? Today both are silent, and the answer is security-relevant for anyone running a gateway.
  2. Consider whether it should follow a third-party base URL by default. Options: a separate CLASSIFIER_BASE_URL so it can be pinned first-party, or a session-start warning when auto mode's classifier will be served by a non-Anthropic endpoint. Note the privacy/cost surface too — the ~107KB policy prompt is sent to that endpoint on every gated action.
  3. Make the outage error name the model actually attempted for classification, not the session model.

Not claimed

  • Not a remote vulnerability, and not reachable without the user configuring their own base URL.
  • We have not reviewed Claude Code's source; all of the above is black-box observation from proxy capture on a single host on 2026-07-16.

View original on GitHub ↗