From de9991eae54c0fc59ff2fb4f8c1be312319287b2 Mon Sep 17 00:00:00 2001 From: greyxp1 Date: Wed, 28 Oct 2026 09:17:39 -0400 Subject: [PATCH 1/7] fix(server): auto resume after CLIProxyAPI cooldowns Claude Code ignores Anthropic's rate-limit headers when it authenticates with an API key, so a turn rejected by a proxy such as CLIProxyAPI never learns when to resume. Read the reset time from the proxy's cooldown message instead, even when Claude Code labels the turn as rate limited. --- .../Adapters/ClaudeAdapterV2.test.ts | 7 +++++-- .../Adapters/ClaudeAdapterV2.ts | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts index 9f2e601afa382..2bf82d3643c86 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts @@ -5682,6 +5682,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { { status: 503, cooldown: "999999999999999999999999h", seconds: 0 }, { status: 503, cooldown: "4m 6s", seconds: 0, errorType: "overloaded_error" }, { status: 401, cooldown: "4m 6s", seconds: 0 }, + { status: 429, cooldown: "1h2m3s", seconds: 3723, proxy: true }, ])("recovers wrapped gateway limits with status $status and cooldown $cooldown", (input) => Effect.gen(function* () { const harness = yield* makeWakeHarness; @@ -5699,11 +5700,13 @@ describe("ClaudeAdapterV2 background wake turns", () => { yield* Queue.offerAll(harness.sdkMessages, [ makeAssistantErrorFrame({ uuid: "00000000-0000-4000-8000-000000000652", - error: "server_error", + error: input.proxy ? "rate_limit" : "server_error", }), makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000653", - result: `API Error: ${input.status} [claude/claude-opus-5-5] [429]: {"type":"error","error":{"type":"${input.errorType ?? "rate_limit_error"}","message":"Please try again later."}} (reset after ${input.cooldown}).`, + result: input.proxy + ? `API Error: Request rejected (429) · All credentials for model claude-opus-5-5 are cooling down (reset after ${input.cooldown}) (last error: rate_limit_error: Please try again later.)` + : `API Error: ${input.status} [claude/claude-opus-5-5] [429]: {"type":"error","error":{"type":"${input.errorType ?? "rate_limit_error"}","message":"Please try again later."}} (reset after ${input.cooldown}).`, terminalReason: "api_error", isError: true, apiErrorStatus: input.status, diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index 5c676d684f686..c05fd63bef552 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -3583,14 +3583,17 @@ function providerFailureFromResult( usageLimited = false, ): OrchestrationV2ProviderFailure | null { const gatewayLimit = - failureHint === undefined && - message.subtype === "success" && - message.is_error && - message.terminal_reason === "api_error" && - message.api_error_status === 503 - ? /^API Error: 503 \[[^\]\r\n]+\] \[429\]: \{"type":"error","error":\{"type":"rate_limit_error",[^\r\n]*\} \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec( - message.result, - ) + message.subtype === "success" && message.is_error && message.terminal_reason === "api_error" + ? message.api_error_status === 503 && failureHint === undefined + ? /^API Error: 503 \[[^\]\r\n]+\] \[429\]: \{"type":"error","error":\{"type":"rate_limit_error",[^\r\n]*\} \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec( + message.result, + ) + : message.api_error_status === 429 + ? // CLIProxyAPI rejects every request until its credentials' cooldown ends. + /^API Error: [^\r\n]*\(429\)[^\r\n]* are cooling down[^\r\n]*? \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec( + message.result, + ) + : null : null; if (gatewayLimit) { const seconds = From 4702797d7348ca326235b81881880a5371a61ce4 Mon Sep 17 00:00:00 2001 From: greyxp1 Date: Wed, 28 Oct 2026 21:55:05 -0400 Subject: [PATCH 2/7] fix(server): ignore nested Claude proxy reset durations --- .../Adapters/ClaudeAdapterV2.test.ts | 23 +++++++++++++++---- .../Adapters/ClaudeAdapterV2.ts | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts index 2bf82d3643c86..133d43f6be6b2 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts @@ -5683,6 +5683,14 @@ describe("ClaudeAdapterV2 background wake turns", () => { { status: 503, cooldown: "4m 6s", seconds: 0, errorType: "overloaded_error" }, { status: 401, cooldown: "4m 6s", seconds: 0 }, { status: 429, cooldown: "1h2m3s", seconds: 3723, proxy: true }, + { + status: 429, + cooldown: "nested 1h", + seconds: 0, + proxy: true, + result: + "API Error: Request rejected (429) · All credentials for model claude-opus-5-5 are cooling down (last error: rate_limit_error (reset after 1h))", + }, ])("recovers wrapped gateway limits with status $status and cooldown $cooldown", (input) => Effect.gen(function* () { const harness = yield* makeWakeHarness; @@ -5704,9 +5712,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { }), makeResultFrame({ uuid: "00000000-0000-4000-8000-000000000653", - result: input.proxy - ? `API Error: Request rejected (429) · All credentials for model claude-opus-5-5 are cooling down (reset after ${input.cooldown}) (last error: rate_limit_error: Please try again later.)` - : `API Error: ${input.status} [claude/claude-opus-5-5] [429]: {"type":"error","error":{"type":"${input.errorType ?? "rate_limit_error"}","message":"Please try again later."}} (reset after ${input.cooldown}).`, + result: + input.result ?? + (input.proxy + ? `API Error: Request rejected (429) · All credentials for model claude-opus-5-5 are cooling down (reset after ${input.cooldown}) (last error: rate_limit_error: Please try again later.)` + : `API Error: ${input.status} [claude/claude-opus-5-5] [429]: {"type":"error","error":{"type":"${input.errorType ?? "rate_limit_error"}","message":"Please try again later."}} (reset after ${input.cooldown}).`), terminalReason: "api_error", isError: true, apiErrorStatus: input.status, @@ -5715,7 +5725,10 @@ describe("ClaudeAdapterV2 background wake turns", () => { const terminal = yield* Queue.take(harness.terminalReceipts); assert.equal(terminal.status, "failed"); if (terminal.status !== "failed") return; - assert.equal(terminal.failure.class, input.seconds > 0 ? "usage_limit" : "provider_error"); + assert.equal( + terminal.failure.class, + input.seconds > 0 || input.proxy ? "usage_limit" : "provider_error", + ); if (input.seconds > 0) { assert.equal(terminal.failure.retryable, true); assert.equal( @@ -5725,7 +5738,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { ), ); } else { - assert.isUndefined(terminal.failure.resetAt); + assert.equal(terminal.failure.resetAt, input.proxy ? null : undefined); } }).pipe( Effect.provide( diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index c05fd63bef552..839a74e565527 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -3590,7 +3590,7 @@ function providerFailureFromResult( ) : message.api_error_status === 429 ? // CLIProxyAPI rejects every request until its credentials' cooldown ends. - /^API Error: [^\r\n]*\(429\)[^\r\n]* are cooling down[^\r\n]*? \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec( + /^API Error: [^\r\n]*\(429\)[^\r\n]* are cooling down \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec( message.result, ) : null From 6a6a57bc77d57e40423152111a1e0047a46541e4 Mon Sep 17 00:00:00 2001 From: greyxp1 Date: Sat, 31 Oct 2026 17:22:56 -0400 Subject: [PATCH 3/7] fix(server): use the outer Claude proxy cooldown deadline --- .../Adapters/ClaudeAdapterV2.test.ts | 37 +++++++++++++++++++ .../Adapters/ClaudeAdapterV2.ts | 23 +++++++----- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts index 5e9858d0c34f6..27bad093cfa92 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts @@ -6354,6 +6354,9 @@ describe("ClaudeAdapterV2 background wake turns", () => { { status: 503, cooldown: "4m 6s", seconds: 0, errorType: "overloaded_error" }, { status: 401, cooldown: "4m 6s", seconds: 0 }, { status: 429, cooldown: "1h2m3s", seconds: 3723, proxy: true }, + { status: 429, cooldown: "1h2m3s", seconds: 3723, proxy: true, sdkResetSeconds: 60 }, + { status: 429, cooldown: "1h2m3s", seconds: 7200, proxy: true, sdkResetSeconds: 7200 }, + { status: 429, cooldown: "1h2m3s", seconds: 3723, proxy: true, sdkResetSeconds: null }, { status: 429, cooldown: "nested 1h", @@ -6362,6 +6365,22 @@ describe("ClaudeAdapterV2 background wake turns", () => { result: "API Error: Request rejected (429) · All credentials for model claude-opus-5-5 are cooling down (last error: rate_limit_error (reset after 1h))", }, + { + status: 429, + cooldown: "nested cooldown", + seconds: 0, + proxy: true, + result: + "API Error: Request rejected (429) · All credentials for model claude-opus-5-5 are cooling down (last error: All credentials for model upstream are cooling down (reset after 1h))", + }, + { + status: 429, + cooldown: "outer cooldown", + seconds: 60, + proxy: true, + result: + "API Error: Request rejected (429) · All credentials for model claude-opus-5-5 are cooling down (reset after 1m) (last error: All credentials for model upstream are cooling down (reset after 1h))", + }, ])("recovers wrapped gateway limits with status $status and cooldown $cooldown", (input) => Effect.gen(function* () { const harness = yield* makeWakeHarness; @@ -6377,6 +6396,24 @@ describe("ClaudeAdapterV2 background wake turns", () => { }), ); yield* Queue.offerAll(harness.sdkMessages, [ + ...(input.sdkResetSeconds !== undefined + ? [ + claudeSdkFrame({ + type: "rate_limit_event", + rate_limit_info: { + status: "rejected", + rateLimitType: "five_hour", + ...(input.sdkResetSeconds === null + ? {} + : { + resetsAt: DateTime.toEpochMillis(now) / 1000 + input.sdkResetSeconds, + }), + }, + uuid: "00000000-0000-4000-8000-000000000651", + session_id: WAKE_NATIVE_SESSION, + }), + ] + : []), makeAssistantErrorFrame({ uuid: "00000000-0000-4000-8000-000000000652", error: input.proxy ? "rate_limit" : "server_error", diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index 31d486693b6c1..783b7871d6737 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -3767,7 +3767,7 @@ function providerFailureFromResult( ) : message.api_error_status === 429 ? // CLIProxyAPI rejects every request until its credentials' cooldown ends. - /^API Error: [^\r\n]*\(429\)[^\r\n]* are cooling down \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec( + /^API Error: Request rejected \(429\) · All credentials for model [^\s()]+ are cooling down \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec( message.result, ) : null @@ -9365,6 +9365,15 @@ export const makeClaudeAdapterV2 = Effect.fn("makeClaudeAdapterV2")(function* ( usageLimited && message.subtype === "success" && message.local_command === "compact" ? context.compactionFailure : null; + const resultFailure = interrupted + ? null + : limitedCompaction !== null + ? makeProviderFailure({ + class: "usage_limit", + message: limitedCompaction.message ?? failureHint, + code: "compaction_failed", + }) + : providerFailureFromResult(message, completedAt, failureHint, usageLimited); const resetTimes = Array.from(context.rateLimitResetTimes.values()); const assistantLimit = context.latestAssistantRateLimit; if ( @@ -9373,19 +9382,13 @@ export const makeClaudeAdapterV2 = Effect.fn("makeClaudeAdapterV2")(function* ( ) { resetTimes.push(assistantLimit.resetAt); } + if (resultFailure?.class === "usage_limit" && resultFailure.resetAt != null) { + resetTimes.push(resultFailure.resetAt); + } const resetAt = resetTimes.length > 0 && resetTimes.every((time) => time !== null) ? resetTimes.reduce((latest, time) => (time! > latest ? time! : latest), "") : null; - const resultFailure = interrupted - ? null - : limitedCompaction !== null - ? makeProviderFailure({ - class: "usage_limit", - message: limitedCompaction.message ?? failureHint, - code: "compaction_failed", - }) - : providerFailureFromResult(message, completedAt, failureHint, usageLimited); const terminalFailure = resultFailure?.class === "usage_limit" ? { ...resultFailure, resetAt: resetAt ?? resultFailure.resetAt ?? null } From b1eadb768eaa75feed85f0a8e926645cad77090d Mon Sep 17 00:00:00 2001 From: greyxp1 Date: Sat, 31 Oct 2026 17:31:37 -0400 Subject: [PATCH 4/7] fix(server): preserve unknown Claude reset deadlines --- .../src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts | 2 +- apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts index 27bad093cfa92..d3e2a3ef14f84 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts @@ -6356,7 +6356,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { { status: 429, cooldown: "1h2m3s", seconds: 3723, proxy: true }, { status: 429, cooldown: "1h2m3s", seconds: 3723, proxy: true, sdkResetSeconds: 60 }, { status: 429, cooldown: "1h2m3s", seconds: 7200, proxy: true, sdkResetSeconds: 7200 }, - { status: 429, cooldown: "1h2m3s", seconds: 3723, proxy: true, sdkResetSeconds: null }, + { status: 429, cooldown: "1h2m3s", seconds: 0, proxy: true, sdkResetSeconds: null }, { status: 429, cooldown: "nested 1h", diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index 783b7871d6737..bd6e5e75260e3 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -9391,7 +9391,7 @@ export const makeClaudeAdapterV2 = Effect.fn("makeClaudeAdapterV2")(function* ( : null; const terminalFailure = resultFailure?.class === "usage_limit" - ? { ...resultFailure, resetAt: resetAt ?? resultFailure.resetAt ?? null } + ? { ...resultFailure, resetAt } : resultFailure; yield* finalizeActiveTurn({ context, From de6911dd9409d8de8519728bd4c953cb63345e77 Mon Sep 17 00:00:00 2001 From: greyxp1 Date: Mon, 9 Nov 2026 21:52:59 -0500 Subject: [PATCH 5/7] fix(server): bound Claude cooldown parsing --- .../server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index b446b5f5af125..d98f54875330d 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -3796,16 +3796,18 @@ function providerFailureFromResult( failureHint?: string, usageLimited = false, ): OrchestrationV2ProviderFailure | null { + // Keep malformed provider output from making regex matching unbounded. + const resultForGatewayParsing = message.result.slice(0, 4096); const gatewayLimit = message.subtype === "success" && message.is_error && message.terminal_reason === "api_error" ? message.api_error_status === 503 && failureHint === undefined ? /^API Error: 503 \[[^\]\r\n]+\] \[429\]: \{"type":"error","error":\{"type":"rate_limit_error",[^\r\n]*\} \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec( - message.result, + resultForGatewayParsing, ) : message.api_error_status === 429 ? // CLIProxyAPI rejects every request until its credentials' cooldown ends. /^API Error: Request rejected \(429\) · All credentials for model [^\s()]+ are cooling down \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec( - message.result, + resultForGatewayParsing, ) : null : null; From 991f93757299a4b16f668ec3715f794d594c61a6 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Tue, 10 Nov 2026 08:12:56 +0000 Subject: [PATCH 6/7] fix(server): narrow Claude result before cooldown parsing --- apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index 118ef4d1ebd0d..c6dbfe5931f26 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -4113,7 +4113,8 @@ function providerFailureFromResult( usageLimited = false, ): OrchestrationV2ProviderFailure | null { // Keep malformed provider output from making regex matching unbounded. - const resultForGatewayParsing = message.result.slice(0, 4096); + const resultForGatewayParsing = + message.subtype === "success" ? message.result.slice(0, 4096) : ""; const gatewayLimit = message.subtype === "success" && message.is_error && message.terminal_reason === "api_error" ? message.api_error_status === 503 && failureHint === undefined From b6db35cfb71eee41523887535076da6b1d93def7 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Tue, 10 Nov 2026 08:23:40 +0000 Subject: [PATCH 7/7] fix(server): reuse main's 429 cooldown path for proxy errors --- .../Adapters/ClaudeAdapterV2.ts | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index c6dbfe5931f26..56b1e92e577bd 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -4112,23 +4112,19 @@ function providerFailureFromResult( failureHint?: string, usageLimited = false, ): OrchestrationV2ProviderFailure | null { - // Keep malformed provider output from making regex matching unbounded. - const resultForGatewayParsing = - message.subtype === "success" ? message.result.slice(0, 4096) : ""; - const gatewayLimit = - message.subtype === "success" && message.is_error && message.terminal_reason === "api_error" - ? message.api_error_status === 503 && failureHint === undefined - ? /^API Error: 503 \[[^\]\r\n]+\] \[429\]: \{"type":"error","error":\{"type":"rate_limit_error",[^\r\n]*\} \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec( - resultForGatewayParsing, - ) - : message.api_error_status === 429 - ? // CLIProxyAPI rejects every request until its credentials' cooldown ends. - /^API Error: Request rejected \(429\) · All credentials for model [^\s()]+ are cooling down \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec( - resultForGatewayParsing, - ) - : null - : null; - const gatewayResetAt = gatewayCooldownResetAt(gatewayLimit, receivedAt); + const gatewayResetAt = + failureHint === undefined && + message.subtype === "success" && + message.is_error && + message.terminal_reason === "api_error" && + message.api_error_status === 503 + ? gatewayCooldownResetAt( + /^API Error: 503 \[[^\]\r\n]+\] \[429\]: \{"type":"error","error":\{"type":"rate_limit_error",[^\r\n]*\} \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec( + message.result, + ), + receivedAt, + ) + : undefined; if (gatewayResetAt !== undefined) { return makeProviderFailure({ class: "usage_limit", @@ -4167,11 +4163,15 @@ function providerFailureFromResult( const apiErrorStatus = message.api_error_status ?? null; // API-key sessions get no rate_limit_info, so a gateway's 429 carries its reset only as text. const resetAt = - apiErrorStatus === 429 && - message.result.startsWith("API Error:") && - !message.result.startsWith("API Error: Request rejected (429) · All credentials for model ") + apiErrorStatus === 429 && message.result.startsWith("API Error:") ? gatewayCooldownResetAt( - /\(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/.exec(message.result), + // CLIProxyAPI nests upstream errors in its message; only its own cooldown counts. + (message.result.startsWith( + "API Error: Request rejected (429) · All credentials for model ", + ) + ? /^API Error: Request rejected \(429\) · All credentials for model [^\s()]+ are cooling down \(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/ + : /\(reset after (?:(\d+)h ?)?(?:(\d+)m ?)?(?:(\d+)s)?\)/ + ).exec(message.result), receivedAt, ) : undefined;