Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
buildRateLimitCooldownMessage() does not check isPureBillingSummary(err) or isBillingErrorMessage(message), so billing failures (e.g., exhausted API balance) are surfaced to the user as rate-limit errors: "⚠️ All models are temporarily rate-limited. Please try again in a few minutes." The misleading message tells the user to wait when they should top up their balance instead.
Steps to reproduce
Configure OpenClaw with a primary model provider and a fallback provider whose API balance is exhausted (negative balance / no credits).
Trigger a model request that causes the primary to fail (e.g., rate-limited) so the fallback is attempted.
Observe the user-visible error message.
Concrete repro from our instance:
agents.defaults.model.primary: "openrouter/deepseek/deepseek-v4-pro" (hit OpenRouter rate limit)
agents.defaults.model.fallbacks: ["openai/gpt-5.4"] (OpenAI balance was negative)
User message: "включи в кабинете теплый свет максимальной яркости"
Result: received "⚠️ All models are temporarily rate-limited. Please try again in a few minutes."
Trajectory evidence: RUN 22 had finalStatus: "success", assistantTexts: [], zero token usage — the model was never actually called, the runtime generated the misleading text.
Steps to reproduce
Configure OpenClaw with a primary model provider and a fallback provider whose API balance is exhausted (negative balance / no credits).
Trigger a model request that causes the primary to fail (e.g., rate-limited) so the fallback is attempted.
Observe the user-visible error message.
Concrete repro from our instance:
agents.defaults.model.primary: "openrouter/deepseek/deepseek-v4-pro" (hit OpenRouter rate limit)
agents.defaults.model.fallbacks: ["openai/gpt-5.4"] (OpenAI balance was negative)
User message: "включи в кабинете теплый свет максимальной яркости"
Result: received "⚠️ All models are temporarily rate-limited. Please try again in a few minutes."
Trajectory evidence: RUN 22 had finalStatus: "success", assistantTexts: [], zero token usage — the model was never actually called, the runtime generated the misleading text.
Expected behavior
When all fallback attempts fail with reason: "billing", the user should see a billing-specific message such as BILLING_ERROR_USER_MESSAGE (already defined in the codebase: "⚠️ API provider returned a billing error — your API key has run out of credits or has an insufficient balance..."). The user should NOT be told to "try again in a few minutes" for an exhausted balance.
Actual behavior
The user sees "⚠️ All models are temporarily rate-limited. Please try again in a few minutes." regardless of whether the underlying failures are rate-limit, billing, or a mix. In our case, the OpenAI fallback returned a billing error (negative balance), but the message gave no indication of a billing problem.
OpenClaw version
2026.5.7 (npm global, commit 8b2a6e5)
Operating system
Debian GNU/Linux 12 (bookworm), kernel Linux 6.12.85-haos
Install method
npm global (openclaw@2026.5.7)
Model
Primary: openrouter/deepseek/deepseek-v4-pro Fallback: openai/gpt-5.4
Provider / routing chain
Primary: openclaw -> openrouter -> deepseek Fallback: openclaw -> openai
Additional provider/model setup details
agents.defaults.thinkingDefault: "off"
Only two models in runtime catalog (no models allowlist set).
OpenAI fallback balance was exhausted at time of failure.
Logs, screenshots, and evidence
File: dist/agent-runner.runtime-*.js
buildRateLimitCooldownMessage (line 381) — no billing check:
function buildRateLimitCooldownMessage(err) {
const codexUsageLimitMessage = extractCodexUsageLimitErrorMessage(err);
if (codexUsageLimitMessage) return codexUsageLimitMessage;
if (!isFallbackSummaryError(err))
return "⚠️ All models are temporarily rate-limited...";
// ... cooldown expiry logic ...
return "⚠️ All models are temporarily rate-limited...";
}
function isPureBillingSummary(err) {
return isFallbackSummaryError(err)
&& err.attempts.length > 0
&& err.attempts.every(a => a.reason === "billing");
}
const BILLING_ERROR_USER_MESSAGE = formatBillingErrorMessage();
// "⚠️ API provider returned a billing error — your API key has run out
// of credits or has an insufficient balance..."
Impact and severity
No response
Additional information
Suggested fix: add a billing check at the top of buildRateLimitCooldownMessage, similar to the existing Codex usage-limit check:
Copy
function buildRateLimitCooldownMessage(err) {
const codexUsageLimitMessage = extractCodexUsageLimitErrorMessage(err);
if (codexUsageLimitMessage) return codexUsageLimitMessage;
// ADD: check for billing first
const message = formatErrorMessage(err);
if (isFallbackSummaryError(err) && isPureBillingSummary(err))
return BILLING_ERROR_USER_MESSAGE;
if (isBillingErrorMessage(message))
return BILLING_ERROR_USER_MESSAGE;
if (!isFallbackSummaryError(err))
return "⚠️ All models are temporarily rate-limited...";
// ... rest unchanged
}
Last known good: NOT_ENOUGH_INFO (behavior appears to have always been present; the billing-specific message path exists in some callers but not in buildRateLimitCooldownMessage itself).
Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
buildRateLimitCooldownMessage() does not check isPureBillingSummary(err) or isBillingErrorMessage(message), so billing failures (e.g., exhausted API balance) are surfaced to the user as rate-limit errors: "⚠️ All models are temporarily rate-limited. Please try again in a few minutes." The misleading message tells the user to wait when they should top up their balance instead.
Steps to reproduce
Configure OpenClaw with a primary model provider and a fallback provider whose API balance is exhausted (negative balance / no credits).
Trigger a model request that causes the primary to fail (e.g., rate-limited) so the fallback is attempted.
Observe the user-visible error message.
Concrete repro from our instance:
agents.defaults.model.primary: "openrouter/deepseek/deepseek-v4-pro" (hit OpenRouter rate limit)⚠️ All models are temporarily rate-limited. Please try again in a few minutes."
agents.defaults.model.fallbacks: ["openai/gpt-5.4"] (OpenAI balance was negative)
User message: "включи в кабинете теплый свет максимальной яркости"
Result: received "
Trajectory evidence: RUN 22 had finalStatus: "success", assistantTexts: [], zero token usage — the model was never actually called, the runtime generated the misleading text.
Steps to reproduce
Configure OpenClaw with a primary model provider and a fallback provider whose API balance is exhausted (negative balance / no credits).
Trigger a model request that causes the primary to fail (e.g., rate-limited) so the fallback is attempted.
Observe the user-visible error message.
Concrete repro from our instance:
agents.defaults.model.primary: "openrouter/deepseek/deepseek-v4-pro" (hit OpenRouter rate limit)⚠️ All models are temporarily rate-limited. Please try again in a few minutes."
agents.defaults.model.fallbacks: ["openai/gpt-5.4"] (OpenAI balance was negative)
User message: "включи в кабинете теплый свет максимальной яркости"
Result: received "
Trajectory evidence: RUN 22 had finalStatus: "success", assistantTexts: [], zero token usage — the model was never actually called, the runtime generated the misleading text.
Expected behavior
When all fallback attempts fail with reason: "billing", the user should see a billing-specific message such as BILLING_ERROR_USER_MESSAGE (already defined in the codebase: "⚠️ API provider returned a billing error — your API key has run out of credits or has an insufficient balance..."). The user should NOT be told to "try again in a few minutes" for an exhausted balance.
Actual behavior
The user sees "⚠️ All models are temporarily rate-limited. Please try again in a few minutes." regardless of whether the underlying failures are rate-limit, billing, or a mix. In our case, the OpenAI fallback returned a billing error (negative balance), but the message gave no indication of a billing problem.
OpenClaw version
2026.5.7 (npm global, commit 8b2a6e5)
Operating system
Debian GNU/Linux 12 (bookworm), kernel Linux 6.12.85-haos
Install method
npm global (openclaw@2026.5.7)
Model
Primary: openrouter/deepseek/deepseek-v4-pro Fallback: openai/gpt-5.4
Provider / routing chain
Primary: openclaw -> openrouter -> deepseek Fallback: openclaw -> openai
Additional provider/model setup details
agents.defaults.thinkingDefault: "off"
Only two models in runtime catalog (no models allowlist set).
OpenAI fallback balance was exhausted at time of failure.
Logs, screenshots, and evidence
Impact and severity
No response
Additional information
Suggested fix: add a billing check at the top of buildRateLimitCooldownMessage, similar to the existing Codex usage-limit check:
Copy
function buildRateLimitCooldownMessage(err) {
const codexUsageLimitMessage = extractCodexUsageLimitErrorMessage(err);
if (codexUsageLimitMessage) return codexUsageLimitMessage;
}
Last known good: NOT_ENOUGH_INFO (behavior appears to have always been present; the billing-specific message path exists in some callers but not in buildRateLimitCooldownMessage itself).