[BUG][Bedrock] `x-anthropic-billing-header` text injected into system prompt causes 400 on Bedrock

Resolved 💬 11 comments Opened Feb 8, 2026 by ebrindley Closed Feb 20, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Claude Code v2.1.37 unconditionally injects the string x-anthropic-billing-header: cc_version=...; cc_entrypoint=...; cch=00000; as a text block in the system prompt content array. When the backend is Amazon Bedrock, the API rejects this with:

400 x-anthropic-billing-header is a reserved keyword and may not be used in the system prompt.

This is distinct from the anthropic-beta HTTP header issues reported in #22893, #21625, #11672, etc. Those are about unsupported HTTP headers. This is about reserved keyword text content being placed inside the system prompt body itself.

Evidence from Binary Analysis

The billing header is assembled and placed into the system prompt unconditionally in the compiled binary:

// 1. Generated as a string:
B = `x-anthropic-billing-header: cc_version=${VERSION}.${hash}; cc_entrypoint=${entrypoint}; cch=00000;`

// 2. In the system prompt assembly function, it's identified and placed as the first text block:
if (q.startsWith("x-anthropic-billing-header")) captured = q;
// ...
if (captured) blocks.push({text: captured, cacheScope: null});

This injection is not gated by:

  • CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS
  • ENABLE_TOOL_SEARCH
  • Platform detection (bedrock/vertex/firstParty)
  • Any environment variable

Debug Log Confirmation

From ~/.claude/debug/ on two separate failing sessions:

Session 1 (variant execution via --print):

[DEBUG] Tool search disabled for model 'us.anthropic.claude-haiku-4-5-20251001-v1:0': model does not support tool_reference blocks.
[DEBUG] attribution header x-anthropic-billing-header: cc_version=2.1.37.981; cc_entrypoint=cli; cch=00000;
[DEBUG] [API:request] Creating client, ANTHROPIC_CUSTOM_HEADERS present: false, has Authorization header: false
...
[ERROR] API error (attempt 1/11): 400 400 x-anthropic-billing-header is a reserved keyword and may not be used in the system prompt.

Session 2 (separate invocation):

[DEBUG] Auto tool search disabled: 1178 tokens (threshold: 20000, 10% of context) [source: query]
[DEBUG] attribution header x-anthropic-billing-header: cc_version=2.1.37.fbe; cc_entrypoint=cli; cch=00000;
[DEBUG] [API:request] Creating client, ANTHROPIC_CUSTOM_HEADERS present: false, has Authorization header: false
...
[ERROR] API error (attempt 1/11): 400 400 x-anthropic-billing-header is a reserved keyword and may not be used in the system prompt.

Key observations:

  • ANTHROPIC_CUSTOM_HEADERS present: false — not caused by custom headers
  • Tool search was already disabled in both sessions — ENABLE_TOOL_SEARCH=0 would not help
  • The billing header hash suffix changes between calls in the same session (.981 vs .fbe)

What Should Happen?

When the backend is Bedrock, Claude Code should either:

  1. Not inject the billing header string into the system prompt text, or
  2. Send it through a mechanism that Bedrock accepts (e.g., HTTP headers or request metadata), or
  3. Provide an environment variable to suppress it

Steps to Reproduce

  1. Configure Claude Code to use Amazon Bedrock (models in settings.local.json with us.anthropic.* IDs)
  2. Run in --print mode: claude --print --output-format json --max-turns 100 - (stdin prompt)
  3. Observe 400 x-anthropic-billing-header is a reserved keyword and may not be used in the system prompt.
  4. Confirm in ~/.claude/debug/<session-id>.txt that the billing header was generated and ANTHROPIC_CUSTOM_HEADERS present: false

The error is somewhat intermittent — the billing header hash changes between requests, and not every request triggers the rejection. In my testing, the first request in a session (typically a token-counting call to Haiku) often succeeds, while the main model request fails.

Workarounds Attempted (None Effective)

| Workaround | Result |
|---|---|
| CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 | No effect — billing header injection is not gated by this flag |
| ENABLE_TOOL_SEARCH=0 | No effect — tool search was already auto-disabled in failing sessions |
| Stripping ANTHROPIC_CUSTOM_HEADERS from env | No effect — the header is generated internally, not from env |
| ANTHROPIC_CUSTOM_HEADERS was already false | Confirmed in debug logs |

modelList Configuration

{
  "modelList": [
    {"id": "us.anthropic.claude-opus-4-6-v1", "name": "Claude Opus 4.6", "provider": "bedrock"},
    {"id": "global.anthropic.claude-opus-4-5-20251101-v1:0", "name": "Claude Opus 4.5", "provider": "bedrock"},
    {"id": "global.anthropic.claude-sonnet-4-5-20251101-v1:0", "name": "Claude Sonnet 4.5", "provider": "bedrock"},
    {"id": "global.anthropic.claude-haiku-4-5-20251101-v1:0", "name": "Claude Haiku 4.5", "provider": "bedrock"}
  ]
}

Claude Model

Opus 4.6 (Bedrock: us.anthropic.claude-opus-4-6-v1)

Is this a regression?

I don't know — the billing header appears to have been present since at least v2.1.29 based on debug logs.

Claude Code Version

2.1.37

Platform

Amazon Bedrock

Operating System

macOS (Darwin 25.2.0)

Terminal/Shell

CLI (--print mode, non-interactive)

View original on GitHub ↗

This issue has 11 comments on GitHub. Read the full discussion on GitHub ↗