Keep the OAuth identity as the first system block - #153
Merged
Conversation
The endpoint classifies a subscription-OAuth request as first-party only when the exact identity line is the first, separate top-level system block. When it is not, the request is answered with an opaque 429 rate_limit_error carrying message "Error", even though the account has quota left (#149). Compression rebuilds `system`, and it was putting the identity back too late. extractSystemText splits blocks by cache_control: once any text block carries a marker, every unmarked block is diverted to `kept` and re-emitted after the dynamic text. The identity block is unmarked — Claude Code marks only the big instruction block — so it landed behind `# Environment`. Before: [ { text: "# Environment\n..." }, { text: "You are Claude Code, Anthropic's official CLI for Claude." } ] After: [ { text: "You are Claude Code, Anthropic's official CLI for Claude." }, { text: "# Environment\n..." } ] Also recognizes the Agent SDK identity, "You are a Claude agent, built on Anthropic's Claude Agent SDK.", which the sdk-cli entrypoint sends instead: `claude -p` and the VS Code extension. That is why the reporter saw this fail in VS Code while the terminal kept working.
Claude Code 2.1.220 picks one of three identity lines, not two:
if (vertex) return CLI;
if (nonInteractive) return appendSystemPrompt ? CLI_WITHIN_SDK : AGENT_SDK;
return CLI;
`--append-system-prompt` (any entrypoint) sends "You are Claude Code,
Anthropic's official CLI for Claude, running within the Claude Agent SDK.",
which was unrecognized and got imaged like ordinary text -> 429.
That line has the plain CLI line as a prefix, so the inline `startsWith`
scan would match the short one and emit a truncated identity:
Before:
{ text: "You are Claude Code, Anthropic's official CLI for Claude." }
<image: ", running within the Claude Agent SDK.\n\n# Environment...">
After:
{ text: "You are Claude Code, Anthropic's official CLI for Claude, running within the Claude Agent SDK." }
<image: "# Environment...">
Sorting the table longest-first makes the scan pick the exact line.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #149
Fixes #150
Problem
Every request through pxpipe fails with a 429 whose body says nothing:
{"type":"error","error":{"type":"rate_limit_error","message":"Error"},"request_id":"..."}The account has quota left, and the same prompt succeeds without the proxy. The endpoint classifies a subscription-OAuth request as first-party only when the exact identity line is the first, separate top-level system block; an unclassified request is rejected as if rate-limited.
Cause
Compression rebuilds
system, and the identity was landing too late.extractSystemTextsplits blocks oncache_control: once any text block carries a marker, every unmarked block is diverted tokeptand re-emitted after the dynamic text. Claude Code marks only the big instruction block, so the unmarked identity block ended up behind# Environment.Before:
After:
The identity is lifted out of
keptbefore assembly, so it leads regardless of whether it arrived marked. The rest of the array keeps its existing order —# Environmentstays last on purpose, since it churns every turn and would otherwise invalidate the cached prefix.VS Code
The reporter saw this only in VS Code, with the terminal working. Claude Code 2.1.220 picks one of three identity lines by entrypoint, and only the first was recognized:
You are Claude Code, Anthropic's official CLI for Claude.claude -p, VS Code extensionYou are a Claude agent, built on Anthropic's Claude Agent SDK.--append-system-promptYou are Claude Code, Anthropic's official CLI for Claude, running within the Claude Agent SDK.All three are matched now. The table is sorted longest-first because the third line has the first as a prefix, and the inline scan uses
startsWith— shortest-first would emit a truncated identity and image the remainder.Test
Reproduced against the live endpoint through pxpipe with the payload captured from the failing session: identity trailing or truncated -> 429 on every attempt; exact identity leading -> 200.
pnpm typecheckandpnpm test(899 tests) pass.