Skip to content

Commit 2ea757c

Browse files
committed
fix: classify mixed billing fallback summaries
1 parent 1e47296 commit 2ea757c

3 files changed

Lines changed: 8 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ Docs: https://docs.openclaw.ai
7272
- Control UI/usage: truncate long context skill, tool, and file names in the usage panel while keeping the full name available on hover. (#42197) Thanks @Rain120.
7373
- Codex: respect explicit `models auth order set` and `config.auth.order` precedence over stale `lastGood` in `/codex account`, and show `no working credential` when every explicit-order profile is ineligible instead of marking a lower-ranked profile as active. Fixes #84386. (#84412) Thanks @openperf.
7474
- Agents: honor `messages.suppressToolErrors` for mutating tool failures so configured chat surfaces do not receive separate warning payloads. (#81561) Thanks @moeedahmed.
75+
- Agents/fallback: surface billing guidance for mixed rate-limit plus billing fallback exhaustion instead of generic failure copy. Fixes #79396. (#79489) Thanks @aayushprsingh.
7576

7677
## 2026.5.19
7778

src/auto-reply/reply/agent-runner-execution.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3549,7 +3549,7 @@ describe("runAgentTurnWithFallback", () => {
35493549
});
35503550
});
35513551

3552-
it("does not show a rate-limit countdown for mixed-cause fallback exhaustion", async () => {
3552+
it("surfaces billing guidance for mixed-cause fallback exhaustion", async () => {
35533553
state.runWithModelFallbackMock.mockRejectedValueOnce(
35543554
Object.assign(
35553555
new Error(
@@ -3593,7 +3593,7 @@ describe("runAgentTurnWithFallback", () => {
35933593

35943594
expect(result.kind).toBe("final");
35953595
if (result.kind === "final") {
3596-
expect(result.payload.text).toBe(GENERIC_RUN_FAILURE_TEXT);
3596+
expect(result.payload.text).toBe("billing");
35973597
expect(result.payload.text).not.toContain("All models failed");
35983598
expect(result.payload.text).not.toContain("402 (billing)");
35993599
expect(result.payload.text).not.toContain("Rate-limited");

src/auto-reply/reply/agent-runner-execution.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ function buildRateLimitCooldownMessage(err: unknown): string {
380380
if (codexUsageLimitMessage) {
381381
return codexUsageLimitMessage;
382382
}
383-
if (isFallbackSummaryError(err) && isPureBillingSummary(err)) {
383+
if (isFallbackSummaryError(err) && hasBillingAttemptSummary(err)) {
384384
return BILLING_ERROR_USER_MESSAGE;
385385
}
386386
const message = formatErrorMessage(err);
@@ -455,11 +455,11 @@ function isPureTransientRateLimitSummary(err: unknown): boolean {
455455
);
456456
}
457457

458-
function isPureBillingSummary(err: unknown): boolean {
458+
function hasBillingAttemptSummary(err: unknown): boolean {
459459
return (
460460
isFallbackSummaryError(err) &&
461461
err.attempts.length > 0 &&
462-
err.attempts.every((attempt) => attempt.reason === "billing")
462+
err.attempts.some((attempt) => attempt.reason === "billing")
463463
);
464464
}
465465

@@ -632,7 +632,7 @@ export function buildKnownAgentRunFailureReplyPayload(params: {
632632
const message = formatErrorMessage(params.err);
633633
const isFallbackSummary = isFallbackSummaryError(params.err);
634634
const isBilling = isFallbackSummary
635-
? isPureBillingSummary(params.err)
635+
? hasBillingAttemptSummary(params.err)
636636
: isBillingErrorMessage(message);
637637
if (isBilling) {
638638
return markAgentRunFailureReplyPayload({
@@ -2270,7 +2270,7 @@ export async function runAgentTurnWithFallback(params: {
22702270
}
22712271
const message = formatErrorMessage(err);
22722272
const isBilling = isFallbackSummaryError(err)
2273-
? isPureBillingSummary(err)
2273+
? hasBillingAttemptSummary(err)
22742274
: isBillingErrorMessage(message);
22752275
const isContextOverflow = !isBilling && isLikelyContextOverflowError(message);
22762276
const isCompactionFailure = !isBilling && isCompactionFailureError(message);

0 commit comments

Comments
 (0)