I run a terminal AI agent called Hermes Agent for most of my coding and automation work. It's configured to use my Claude subscription via the same OAuth credential as the claude CLI. One day it started failing with a specific error while the CLI itself worked perfectly:
HTTP 400: You're out of extra usage. Add more at claude.ai/settings/usage and keep going.
That error is Anthropic's way of saying your purchased extra-usage credits are empty. My first thought was the obvious one: I must be out of quota. But the claude CLI still worked — same subscription, same week. And when I checked my usage page, my Claude Code weekly allowance looked untouched. What had actually happened was worse and more interesting: every request from my agent had been billed to extra-usage credits instead of the subscription quota my plan already includes. I'd burned through paid top-ups without touching the allowance I was already paying for. This is the story of how I proved that, and the one-line root cause that explained all of it.
Same token, same account — so why different billing?
Both the CLI and the agent authenticate the same way: with the Claude Code OAuth token stored in my Mac's keychain. I verified this byte-for-byte by pointing the CLI at a local logging proxy and comparing the token it sent against the one the agent uses. Identical. Same account, same token, same model, same API endpoint.
Yet the CLI's requests drew from the subscription, and the agent's drew from extra usage. The only remaining difference was the request itself — the headers, the URL, and the payload. Anthropic's OAuth billing classifier doesn't just look at who you are. It looks at what client you're presenting as, and routes the request to a billing lane based on that fingerprint.

The fingerprint that was wrong
I captured the real CLI's request through a local proxy and diffed it against what my agent was sending. Four things stood out:
- The user-agent. The CLI sends
claude-cli/2.1.233 (external, sdk-cli). The agent sentclaude-code/2.1.74 (external, cli)— a stale product prefix from an old naming scheme, an outdated version, and a different entrypoint label. - Missing query param. The CLI hits
/v1/messages?beta=true. The agent hit/v1/messageswithout it. - A shorter beta list. The CLI sends ten
anthropic-betaheaders (thinking, context management, effort, etc.). The agent sent two. - Two missing markers entirely. The CLI's payload starts its system prompt with a billing-routing header (
x-anthropic-billing-header: cc_version=…; cc_entrypoint=sdk-cli;) and includes ametadata.user_idcarrying the account UUID. The agent sent neither.
Any one of those might be enough to look like a third-party client to the classifier. Together they guaranteed it. The agent was impersonating a slightly-off version of the official client — close enough to authenticate, not close enough to bill like one.

Why the version was stale — a debugging side-quest
There was a second, smaller bug hiding behind the first. The agent tries to detect the installed Claude Code version so it can put a current version in its user-agent. On my machine that detection silently failed, and it fell back to a hardcoded 2.1.74 — from early 2026 — while my actual CLI was 2.1.233.
The cause was a broken duplicate install: /usr/local/bin/claude is a global npm copy that crashes under the current Node version, and it shadows the working binary at ~/.local/bin/claude that my shell actually uses. The version probe hit the broken one, got empty output, and fell back to the stale constant. The fix was updating that constant — and the deeper lesson was already on my to-do list: uninstall the duplicate.
The fix
The agent is open source and installed from source on my machine, so I patched its Anthropic adapter to mirror the CLI's fingerprint exactly:
- User-agent →
claude-cli/<version> (external, sdk-cli) - The full ten-beta header list
?beta=trueon the messages URL- A session-id header
- The
x-anthropic-billing-headerblock at the start of the system prompt metadata.user_idwith the account UUID read from~/.claude.json
Then I ran the test suite, and then the real test:
$ hermes chat -q "Reply with exactly: OK"
→ OK
Log line from that run: API call #1: model=claude-sonnet-5 provider=anthropic latency=2.0s, finished normally, no billing error. The same token that had been draining extra usage all day was now drawing from the subscription. I've been using it since, and the extra-usage balance has stopped moving.
What I'd tell anyone running a tool on a Claude subscription
If you use Claude Code's OAuth credential from anything other than the official CLI — another agent, an IDE extension, a script — and you see "out of extra usage" while the CLI still works, this is the likely cause. A few things that helped me:
- Check the billing lane, not just the balance. If the CLI works and the usage page says your weekly allowance is untouched, you're being billed somewhere else.
- Capture and compare. Point the working client at a local logging proxy, capture its request, and diff it against the failing client's. The difference is the bug.
- Same token isn't the same request. Anthropic's OAuth routing looks at the full client fingerprint — user-agent, beta headers, query params, and payload markers.
I've written up the full diagnosis and the patch so the next person (or future me) doesn't have to re-derive it — the fix is specific to the version of the agent I'm running, but the method applies to any tool using Claude Code's OAuth.
Debugging my own tooling so my infrastructure bills to the plan I already pay for — that's the kind of thing I enjoy getting to the bottom of. Get in touch if you've got a system doing something similar.