fix(llm): the subscription 429s were a malformed request, not a rate limit
deploy / test (push) Successful in 5m18s
deploy / build (push) Successful in 5m14s

On the OAuth path Anthropic requires the Claude Code identity to be its own
first system BLOCK. We concatenated it with the caller's prompt into a single
string, so EVERY server-side call that set a system prompt was rejected — and
the rejection arrives as `429 {"type":"rate_limit_error","message":"Error"}`,
which reads as throttling and is not.

Measured on one token, seconds apart:

    "PREAMBLE"                    (string)  -> 200
    "PREAMBLE\n\nJudge the …"     (string)  -> 429
    "PREAMBLE"                    (string)  -> 200   (control)
    ["PREAMBLE"]                  (blocks)  -> 200
    ["PREAMBLE", "Judge the …"]   (blocks)  -> 200

while the account reported `5h utilization 0.07, 7d 0.11, overage 0.0`, every
window `allowed`. A Max 20x subscription at 7% was being read as out of
capacity.

What this was breaking, silently, for as long as it has been there:
  - every `done_when` verdict on the subscription judge. Mission 01a00bbb
    pass 2 returned "could not evaluate the completion condition this pass"
    and BURNED one of the phase's three passes on it.
  - the boot preflight, which reported `claude-opus-4-8 throttled (configured,
    no capacity now)` on every start — a diagnostic that was itself the bug.
  - mission_refiner, phase_summarizer, swarm planning.

The `claude` CLI was unaffected throughout, because it sends its system prompt
as blocks. That divergence is what made this look like an account problem: the
agents worked while everything server-side "throttled".

After the fix the preflight reports opus-5, sonnet-5 and haiku all `ok`.

The API-key path keeps sending a plain string — it never had this constraint.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-08-16 13:15:27 -07:00
co-authored by Claude Opus 5
parent 69c294addc
commit b31a79f650
+73 -26
View File
@@ -45,16 +45,42 @@ impl AnthropicProvider {
is_setup_token(&self.api_key)
}
/// Prepend the Claude Code identity line, unless the caller's system
/// prompt already opens with it (so repeated wrapping can't stack).
fn with_oauth_preamble(system: &str) -> String {
if system.trim_start().starts_with(OAUTH_SYSTEM_PREAMBLE) {
return system.to_string();
/// The Claude Code identity as its OWN system block, with the caller's
/// prompt as a second block.
///
/// This used to concatenate the two into one string, and **every call that
/// set a system prompt failed**. On the OAuth path Anthropic requires the
/// identity to be the first system BLOCK; a single string that merely
/// begins with it is rejected — and the rejection arrives as
/// `429 {"type":"rate_limit_error","message":"Error"}`, which reads as
/// throttling and is not. Measured on one token, seconds apart:
///
/// ```text
/// "PREAMBLE" (string) -> 200
/// "PREAMBLE\n\nJudge the …" (string) -> 429
/// ["PREAMBLE"] (blocks) -> 200
/// ["PREAMBLE", "Judge the …"] (blocks) -> 200
/// ```
///
/// while the account itself reported `5h utilization 0.07, status allowed`.
/// Every judge verdict, preflight probe and refiner call on a subscription
/// token was failing 100% of the time and being logged as "no capacity",
/// including a `done_when` verdict that failed closed and burned one of a
/// phase's three passes.
///
/// Idempotent: a caller whose prompt already opens with the identity does
/// not get it twice.
fn oauth_system_blocks(system: &str) -> Value {
let mut blocks = vec![json!({"type": "text", "text": OAUTH_SYSTEM_PREAMBLE})];
let rest = system
.trim_start()
.strip_prefix(OAUTH_SYSTEM_PREAMBLE)
.unwrap_or(system)
.trim();
if !rest.is_empty() {
blocks.push(json!({"type": "text", "text": rest}));
}
if system.trim().is_empty() {
return OAUTH_SYSTEM_PREAMBLE.to_string();
}
format!("{OAUTH_SYSTEM_PREAMBLE}\n\n{system}")
Value::Array(blocks)
}
fn wire_messages(request: &ChatRequest) -> Vec<Value> {
@@ -121,10 +147,12 @@ impl LlmProvider for AnthropicProvider {
// Claude Code beta set, and a system prompt whose first line is the
// Claude Code identity. Sending it as `x-api-key` returns 401.
let oauth = is_setup_token(&self.api_key);
// Blocks on the OAuth path, plain string on the API-key path — the
// API-key path never had this constraint and must keep working.
let system = if oauth {
Self::with_oauth_preamble(&request.system)
Self::oauth_system_blocks(&request.system)
} else {
request.system.clone()
Value::String(request.system.clone())
};
let body = json!({
"model": request.model,
@@ -250,25 +278,44 @@ mod tests {
assert!(!is_setup_token(""));
}
/// The identity must be its own FIRST block, and the caller's prompt a
/// SECOND one. Concatenating them into a single string is what made every
/// system-prompt-setting call return `429 rate_limit_error` on a token
/// whose account was at 7% utilization.
#[test]
fn oauth_preamble_is_prepended_once() {
let once = AnthropicProvider::with_oauth_preamble("Judge the condition.");
assert!(once.starts_with(OAUTH_SYSTEM_PREAMBLE));
assert!(once.ends_with("Judge the condition."));
// Re-wrapping must not stack the identity line — the API rejects a
// system prompt that doesn't *start* with it, and duplicating it is
// pure token waste on a path whose whole point is being cheap.
let twice = AnthropicProvider::with_oauth_preamble(&once);
assert_eq!(once, twice);
assert_eq!(twice.matches(OAUTH_SYSTEM_PREAMBLE).count(), 1);
fn the_oauth_system_is_blocks_with_the_identity_first() {
let v = AnthropicProvider::oauth_system_blocks("Judge the condition.");
let blocks = v.as_array().expect("system must be an ARRAY, not a string");
assert_eq!(blocks.len(), 2);
assert_eq!(blocks[0]["type"], "text");
assert_eq!(blocks[0]["text"], OAUTH_SYSTEM_PREAMBLE);
assert_eq!(blocks[1]["text"], "Judge the condition.");
}
/// Idempotent: a caller that already opens with the identity must not send
/// it twice — duplicate identity is token waste on every single call.
#[test]
fn oauth_preamble_handles_an_empty_system_prompt() {
assert_eq!(
AnthropicProvider::with_oauth_preamble(" "),
OAUTH_SYSTEM_PREAMBLE
);
fn the_identity_is_never_duplicated() {
let already = format!("{OAUTH_SYSTEM_PREAMBLE}\n\nJudge the condition.");
let v = AnthropicProvider::oauth_system_blocks(&already);
let blocks = v.as_array().unwrap();
assert_eq!(blocks.len(), 2, "{blocks:?}");
assert_eq!(blocks[0]["text"], OAUTH_SYSTEM_PREAMBLE);
assert_eq!(blocks[1]["text"], "Judge the condition.");
let text = serde_json::to_string(&v).unwrap();
assert_eq!(text.matches(OAUTH_SYSTEM_PREAMBLE).count(), 1);
}
/// An empty caller prompt yields the identity ALONE — never a trailing
/// empty block, which the API rejects.
#[test]
fn an_empty_system_prompt_yields_the_identity_alone() {
for empty in ["", " ", OAUTH_SYSTEM_PREAMBLE] {
let v = AnthropicProvider::oauth_system_blocks(empty);
let blocks = v.as_array().unwrap();
assert_eq!(blocks.len(), 1, "{empty:?} -> {blocks:?}");
assert_eq!(blocks[0]["text"], OAUTH_SYSTEM_PREAMBLE);
}
}
#[test]