Skip to content

Claude Code → Responses translation keeps the per-request billing header, so Muse/OpenAI prompt cache never hits #6627

Closed

Description

@rhomat27

Client or integration

Claude Code

Area

Proxy and routing

Summary

Claude Code requests translated to a Responses upstream get essentially no prompt-cache hits. I saw this with meta-muse (Muse Spark 1.3 Contributor). The translated instructions start with Claude Code's per-request billing line:

x-anthropic-billing-header: cc_version=…; cc_entrypoint=…; cch=…;

cch changes on every request. So the first bytes of the prompt differ every turn, and a prefix cache never matches. Anthropic excludes this line from caching; Muse and OpenAI do not. src/adapters/google.ts already strips it (stripAntigravityBillingHeader). systemToInstructions in src/claude/inbound-content-options.ts does not, and it passes the line through to every translated route. That function is unchanged on dev at 0511f458f1.

The system-derived prompt_cache_key fallback hashes the same system text, so it also rotates per request when a client sends no metadata.user_id.

Reproduction

  1. Route a Claude Code subagent (Desktop entrypoint, first-party mode) to meta-muse/muse-spark-1.3-contributor, directly or through a combo.
  2. Run a 5-turn task, where each turn is one tool call.
  3. With OPENCODEX_CACHE_DEBUG=1, cache-debug.jsonl shows:
    • prompt_cache_key is present and equal on every turn.
    • The tools and the later instruction and message blocks keep stable tags.
    • Only the first instruction block's tag changes on every turn.
    • Upstream cached_tokens is 0 on every turn.
  4. Control: send /v1/responses directly with a stable ~15k-token instructions and a fixed prompt_cache_key. Turns 2–3 cache about 99%. That rules out the account, the key and non-ASCII content.

claudeCode.stabilizePromptCache: true does not help, because it only relocates trailing footers.

Verified fix. Strip a leading x-anthropic-billing-header: line in systemToInstructions, matching only at the prompt start, with the same regex as the Google adapter. Apply it to the string itself for a string system, and to the first text block only for an array system, dropping the block if it ends up empty:

function stripClaudeBillingHeader(text: string): string {
  return text.replace(/^x-anthropic-billing-header:[^\n]*\n*/, "");
}

With that patch, the same 5-turn subagent went from 0 cached on every turn to 21,361, 22,129, 22,257 and 22,385 cached on turns 2–5 (about 99%), on two machines. Native Anthropic passthrough doesn't use this translation and is unaffected.

Version

opencodex 2.76.0, Claude Code 2.1.286

Operating system

macOS

Provider and model

meta-muse / muse-spark-1.3-contributor (also reached through a failover combo)

Logs or error output

usage.jsonl, same Muse account:

  • Codex (inboundProtocol: responses): 96.5% of input cached, over 63,895 requests.
  • Claude Code (inboundProtocol: messages): 0.7% cached, over 15,907 requests, every day since first use.

Redacted configuration

claudeCode.desktopMode: first-party; meta-muse provider adapter: openai-responses, baseUrl: https://api.meta.ai/v1.

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

  1. github-actions commented on Oct 5, 2026

    @github-actions
    Contributor

    Issue reopened

    The report now contains the information required by the automated check. Thanks for updating it.

  2. added
    bugSomething isn't working
    proxyHTTP proxy, routing, reverse-proxy / management auth
    toolstool_calls, MCP, web-search / sidecar tools
    on Oct 5, 2026
  3. lidge-jun commented on Oct 5, 2026

    @lidge-jun
    Owner

    Fixed on dev by #6634 (0889b60): the Messages→Responses translation now strips Claude Code's leading x-anthropic-billing-header line before it becomes instructions, so the translated prefix and the system-derived fallback prompt_cache_key stay stable across turns. Thanks for the precise diagnosis and the verified patch. Closing manually because PRs target dev; it ships in the next release.

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 workingproxyHTTP proxy, routing, reverse-proxy / management authtoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions