gemini-cli - 💡(How to fix) Fix TerminalQuotaError (QUOTA_EXHAUSTED) does not trigger model fallback in headless -p mode (OAuth users) [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
google-gemini/gemini-cli#26840Fetched 2026-05-11 03:26:59
View on GitHub
Comments
1
Participants
2
Timeline
10
Reactions
0
Timeline (top)
labeled ×6added_to_project_v2 ×1commented ×1issue_type_added ×1

When the gemini-2.5-pro daily RPD quota is exhausted in headless -p mode, the CLI exits with code 1 and prints TerminalQuotaError to stderr. There is no fallback to gemini-2.5-flash or gemini-2.5-flash-lite, even though those model quotas are untouched.

Interactive REPL mode falls back correctly, but headless -p mode does not.

Error Message

? (authType, error) => handleFallback(this.config, currentModel, authType, error)

Root Cause

Root Cause Analysis

Code Example

onPersistent429: this.config.isInteractive()
  ? (authType, error) => handleFallback(this.config, currentModel, authType, error)
  : void 0,

---

const handler = config.getFallbackModelHandler();
if (typeof handler !== 'function') return null;
RAW_BUFFERClick to expand / collapse

Summary

When the gemini-2.5-pro daily RPD quota is exhausted in headless -p mode, the CLI exits with code 1 and prints TerminalQuotaError to stderr. There is no fallback to gemini-2.5-flash or gemini-2.5-flash-lite, even though those model quotas are untouched.

Interactive REPL mode falls back correctly, but headless -p mode does not.

Environment

  • OS: Windows 11 Pro (10.0.26200)
  • Node.js: v22.19.0
  • gemini-cli: 0.41.2 (Auto-updated from 0.41.1; possible regression)
  • Shell: PowerShell 7+
  • Auth: Google OAuth (gemini login) — AuthType.USE_GEMINI

Root Cause Analysis

1. The Interactive Gate

In chunk-6DSAZLFF.js (~line 270800), the fallback handler is gated by an interactivity check:

onPersistent429: this.config.isInteractive()
  ? (authType, error) => handleFallback(this.config, currentModel, authType, error)
  : void 0,

Because isInteractive() returns false in headless -p mode, onPersistent429 is explicitly set to undefined. When a TerminalQuotaError is caught in retryWithBackoff, the fallback branch is never entered.

2. Unregistered Fallback Handler

In handleFallback (~line 270272):

const handler = config.getFallbackModelHandler();
if (typeof handler !== 'function') return null;

Even in paths where onPersistent429 is provided, getFallbackModelHandler() is not registered in headless mode, returning null and aborting the fallback.

3. Auth-Specific Impact

For AuthType.USE_GEMINI (OAuth), getRateLimitMessage returns generic "wait" text without the fallback signal. OAuth free-tier users receive no fallback, effectively locking them out of the CLI despite having Flash quota remaining.

Note: RPM spikes (RetryableQuotaError) fall back correctly. This bug is specific to full daily RPD exhaustion (QUOTA_EXHAUSTED / TerminalQuotaError).

Steps to Reproduce

  1. Auth: Log in via gemini login.
  2. Exhaust Quota: Exhaust the 100 RPD limit on gemini-2.5-pro.
  3. Execute: Run gemini -y -p "hello"
  4. Observe: CLI returns TerminalQuotaError, exit code 1. No fallback attempt.
  5. Verify: Run gemini -y --model gemini-2.5-flash -p "hello". This succeeds, proving Flash quota was available.

Expected Behaviour

gemini -y -p "..." hitting quota limits on Pro should automatically retry with Flash or Flash-Lite, matching the interactive REPL behavior.

Actual Behaviour

CLI exits immediately with code 1. Automation scripts fail entirely despite 1,000+ RPD being available on Flash-Lite.

Suggested Fixes

  • Option A (Minimal): Remove the isInteractive() gate so onPersistent429 is always registered. Note: Must ensure the handler is non-prompting in headless mode to prevent hanging scripts/CI pipelines.
  • Option B (Recommended): Register a default getFallbackModelHandler for headless mode that silently and automatically selects the next model in the chain without requiring user confirmation.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING