Skip to content

gateway: a run of OpenRouter 402s cools its key when OpenRouter leads the walk (D261) - #122

Merged
jaredLunde merged 5 commits into
mainfrom
openrouter-402-cooling
Oct 4, 2026
Merged

jaredLunde merged 5 commits into
mainfrom
openrouter-402-cooling

Conversation

@jaredLunde

Copy link
Copy Markdown
Contributor

Closes the gap D258 left: when a row or x-beyond-order puts OpenRouter first, its 402 fails over at the head (body unread), and OpenRouter's 402 is ambiguous (out of credit vs. "this request requires more credits"), so the unfunded key was never cooled and every later request paid a round trip to it.

OpenRouter's API can't settle it with an inference key (/api/v1/key is null for keys without their own limit; /api/v1/credits needs a management key), so this uses a repeat heuristic instead:

  • Each OpenRouter 402 the walk fails over on (both pingora's in-place retry and a FullBody re-run) is a strike against that configured pool key. The 3rd in a row with no 2xx from that key in between cools it via the existing cool_unfunded_key → Provider::mark_key_bad (metric reason unfunded).
  • A 2xx resets the count (one relaxed load; a write only when non-zero). Any cooling spends it. Only the strike that reaches exactly 3 cools, so a burst cools once.
  • State: one AtomicU32 beside each pool key's existing cooldown slot. Per replica, in memory, bounded by configured keys; BYO keys are never touched.
  • Accepted cost: a nearly empty account sent only large requests may cool for KEY_COOLDOWN (60 s).

Tests: reliability_openrouter_402 (3 in a row cool, small + 100 KiB bodies; too-costly 402s between successes never cool; 24 concurrent 402s cool once) and route::tests for per-key isolation, recovery after cooldown, and cooling spending the count. 11/12 hand mutations caught, 1 equivalent (documented). D261 in verify/defects.toml; ARCHITECTURE.md updated.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JimHGjsfk2Ktm5GxyZJKKk

@jaredLunde
jaredLunde enabled auto-merge (squash) October 4, 2026 16:36
jaredLunde and others added 5 commits October 4, 2026 09:50
D258 cools a 402 a catalog walk fails over on by its status alone, but not
OpenRouter's, which may be one request too costly for a funded balance; a
walk that leads with OpenRouter paid a round trip to an unfunded account on
every request. An out-of-band balance check cannot tell the cases apart
(GET /api/v1/key reports only a per-key limit, null for a key drawing on
account credit; GET /api/v1/credits needs a management key).

Each unread OpenRouter 402 is a strike against its pool key (a relaxed
AtomicU32 beside the key's cooldown); the third in a row with no 2xx from
the key between them cools it through cool_unfunded_key (same metric
reason, same warn line). Any 2xx resets the count (one relaxed load, a
store only when non-zero), any cooling spends it, and a key already
cooling does not count, so a burst cools it once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JimHGjsfk2Ktm5GxyZJKKk
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JimHGjsfk2Ktm5GxyZJKKk
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JimHGjsfk2Ktm5GxyZJKKk
Kills the changed-lines survivor that made any candidate refusal (401/403) a strike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JimHGjsfk2Ktm5GxyZJKKk
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JimHGjsfk2Ktm5GxyZJKKk
@jaredLunde
jaredLunde force-pushed the openrouter-402-cooling branch from a914073 to d830e80 Compare October 4, 2026 16:52
@jaredLunde
jaredLunde merged commit b5df993 into main Oct 4, 2026
16 of 19 checks passed
@jaredLunde
jaredLunde deleted the openrouter-402-cooling branch October 4, 2026 17:08
jaredLunde added a commit that referenced this pull request Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant