Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/server/messages-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,17 @@ export async function handleNativeMessages(options: HandleNativeMessagesOptions)
}
releaseRetainedRequest();

// The last pool account refused on spent quota and rotation found no replacement: answer the
// usage limit with its reset (#6991), as the bridge lane does, not the upstream "try again later".
const poolUsageLimit = response.status === 429 && oauthBinding
? anthropicRoutingFor(nativeInstance!).anthropicPoolUsageLimitAfterRefusal(Date.now(), oauthBinding.routeDecision, route.modelId)
: null;
if (poolUsageLimit) {
discard(response);
cleanupAbort();
logCtx.errorCode = "usage_limit_reached";
return retainUpstreamMessagesRequestId(localOAuthFailure(poolUsageLimit)!, response.headers);
}
if (!response.ok) {
let bodyText = "";
try {
Expand Down
27 changes: 27 additions & 0 deletions tests/claude-integration/messages-native-oauth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,33 @@ test("shared quota exhaustion is bounded and never resends an already-tried alte
expect(new Set(sent.map(entry => entry.headers.get("authorization"))).size).toBe(sent.length);
});

test("a pool whose every account refuses on spent quota answers the usage limit and its reset (#6991 parity)", async () => {
await seed(2);
const config = fixtureConfig();
config.anthropicAccountPool = { enabled: true };
const resetSeconds = Math.floor(Date.now() / 1000) + 7_200;
config.providers.anthropic!.fetch = (async (input, init) => {
sent.push({ url: String(input), headers: new Headers(init?.headers), body: JSON.parse(String(init?.body)) });
return Response.json({ type: "error", error: { type: "rate_limit_error", message: "fixture window spent" } }, {
status: 429,
headers: {
"anthropic-ratelimit-unified-7d-status": "rejected", "anthropic-ratelimit-unified-7d-reset": String(resetSeconds),
"retry-after": "7200", "request-id": "req_fixture_spent",
},
});
}) as typeof fetch;
const { response, text, row } = await send(config, { ...BODY, stream: false });
expect(response.status).toBe(429);
expect(sent).toHaveLength(2);
expect(JSON.parse(text).error).toMatchObject({
type: "rate_limit_error",
message: `All Anthropic OAuth accounts have reached their usage limit; the first one resets at ${new Date(resetSeconds * 1000).toISOString()}`,
});
expect(Number(response.headers.get("retry-after"))).toBeGreaterThan(7_000);
expect(response.headers.get("request-id")).toBe("req_fixture_spent");
expect(row.protocolTrace?.mode).toBe("native");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});

test("family lease releases when spend admission refuses without a physical send", async () => {
await seed(1);
const config = fixtureConfig();
Expand Down
Loading