Skip to content

fix(pr-review): provider usage-cap not detected — cascade hard-fails instead of falling back #106

Description

@don-petry

Summary

When Claude hits its subscription/usage-cap (distinct from a per-minute rate limit), the triage process exits with code 1 but the is_rate_limited regex in engine.sh doesn't match Claude Code's usage-limit message phrasing. The cascade treats this as a fatal error instead of triggering the engine-fallback chain (claude → gemini → copilot).

Observed in run: https://github.com/petry-projects/.github-private/actions/runs/25648497571/job/75282153268

Root cause (two parts)

1. Incomplete is_rate_limited regex

Claude Code's usage-cap message uses phrasing not covered by the current regex (e.g. out of tokens, claude.*usage, plan limit, HTTP 402/529). The function only checks for API-level rate-limit text, not subscription/billing cap text.

2. Rate-limit check only inspects stdout

run_triage redirects stderr to $TRIAGE_LOG and stdout is captured in $TRIAGE_RESULT. The is_rate_limited check only runs on $TRIAGE_RESULT — some providers write their error to stderr instead, so a cap on those providers also falls through to the hard-fail path.

3. Triage stdout swallowed on hard-fail (makes diagnosis impossible)

In the failure path, only $TRIAGE_LOG (stderr) is printed. $TRIAGE_RESULT (stdout) — where Claude Code writes its error — is silently discarded, making the failure opaque in CI logs.

Impact

Any time Claude hits its usage cap the entire PR-review session aborts and retries on the next hourly schedule, rather than immediately retrying with Copilot or Gemini. The fallback chain that already exists in review-batch.sh is never reached.

Fix (tracked in linked PR)

  1. scripts/engine.sh — expand is_rate_limited to cover usage/billing-cap patterns and 402/529 status codes.
  2. scripts/review-one-pr.sh — also pass $TRIAGE_LOG (stderr) through is_rate_limited; print both channels in the hard-fail path for diagnostics.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions