[BUG][Bedrock] `x-anthropic-billing-header` text injected into system prompt causes 400 on Bedrock
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_BETASENABLE_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=0would not help - The billing header hash suffix changes between calls in the same session (
.981vs.fbe)
What Should Happen?
When the backend is Bedrock, Claude Code should either:
- Not inject the billing header string into the system prompt text, or
- Send it through a mechanism that Bedrock accepts (e.g., HTTP headers or request metadata), or
- Provide an environment variable to suppress it
Steps to Reproduce
- Configure Claude Code to use Amazon Bedrock (models in
settings.local.jsonwithus.anthropic.*IDs) - Run in
--printmode:claude --print --output-format json --max-turns 100 -(stdin prompt) - Observe
400 x-anthropic-billing-header is a reserved keyword and may not be used in the system prompt. - Confirm in
~/.claude/debug/<session-id>.txtthat the billing header was generated andANTHROPIC_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)
This issue has 11 comments on GitHub. Read the full discussion on GitHub ↗