Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ project/
### Error Pattern Detection

Configurable patterns detect rate limit and quota errors in claude/codex output:
- `claude_error_patterns` / `codex_error_patterns`: comma-separated error patterns (default strings in `llms.txt` and the embedded config). Codex phrases are tightened so review findings that *talk about* rate limiting do not trip a false positive. The claude error set enumerates `API Error:` by code (`400/401/403/404/413/429/500`) rather than the bare substring, so case-insensitive matching does not abort a successful run on narrated `API error:` prose (e.g. a ticket title or a quoted error string); the transient `529/502/503/504` codes are handled by `claude_retry_patterns`, not here
- `claude_error_patterns` / `codex_error_patterns`: comma-separated error patterns (default strings in the embedded config, mirrored in the README config table; not carried in `llms.txt`). Codex phrases are tightened so review findings that *talk about* rate limiting do not trip a false positive. The claude error set enumerates `API Error:` by code (`400/401/403/404/413/429/500`) rather than the bare substring, so case-insensitive matching does not abort a successful run on narrated `API error:` prose (e.g. a ticket title or a quoted error string); the transient `529/502/503/504` codes are handled by `claude_retry_patterns`, not here
- Matching is case-insensitive substring search
- Whitespace is trimmed from each pattern
- For claude: patterns checked against the last 10 text blocks (not full output) to avoid false positives when analysis text mentions rate limit phrases. Context cancellation paths bypass pattern checks
Expand All @@ -307,7 +307,7 @@ Transient retry patterns for wrapper-level stalls:
- Retry detection is suppressed when `result.Signal` is non-empty: a completed run that emitted a structured signal (e.g. `ALL_TASKS_DONE`) must not be discarded and re-run just because the output text mentions a retry marker. `patternError(recentText, signal)` (`pkg/executor/executor.go`) gates only the retry tier on the signal; limit and error patterns still fire regardless (they surface loudly rather than silently re-running)

Limit patterns for wait+retry behavior:
- `claude_limit_patterns` / `codex_limit_patterns`: comma-separated limit patterns (default strings in `llms.txt` and the embedded config)
- `claude_limit_patterns` / `codex_limit_patterns`: comma-separated limit patterns (default strings in the embedded config, mirrored in the README config table and `llms.txt`)
- `wait_on_limit`: duration string (e.g., "1h", "30m"), disabled by default
- `--wait` CLI flag overrides `wait_on_limit` config
- Priority: retry patterns checked first, then limit patterns; if a limit pattern matches AND wait > 0, wait and retry; if match AND wait == 0, fall through to error pattern behavior
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -993,9 +993,9 @@ Provider-related CLI flags (`--claude-command`, `--claude-args`, `--external-rev
| `color_signal` | Completion/failure signals color (hex) | `#ff6464` |
| `color_timestamp` | Timestamp prefix color (hex) | `#8a8a8a` |
| `color_info` | Informational messages color (hex) | `#b4b4b4` |
| `claude_error_patterns` | Patterns to detect in claude output (comma-separated) | `You've hit your limit,You've hit your session limit,API Error: 400,API Error: 401,API Error: 403,API Error: 404,API Error: 413,API Error: 429,API Error: 500,cannot be launched inside another Claude Code session,Not logged in,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit` |
| `claude_error_patterns` | Patterns to detect in claude output (comma-separated) | `You've hit your limit,You've hit your session limit,API Error: 400,API Error: 401,API Error: 403,API Error: 404,API Error: 413,API Error: 429,API Error: 500,cannot be launched inside another Claude Code session,Not logged in,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit,You've hit your individual spend limit` |
| `codex_error_patterns` | Patterns to detect in codex output (comma-separated) | `Rate limit exceeded,rate limit reached,429 Too Many Requests,quota exceeded,insufficient_quota,You've hit your usage limit` |
| `claude_limit_patterns` | Limit patterns for claude triggering wait+retry (comma-separated) | `You've hit your limit,You've hit your session limit,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit` |
| `claude_limit_patterns` | Limit patterns for claude triggering wait+retry (comma-separated) | `You've hit your limit,You've hit your session limit,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit,You've hit your individual spend limit` |
| `codex_limit_patterns` | Limit patterns for codex triggering wait+retry (comma-separated) | `Rate limit exceeded,rate limit reached,429 Too Many Requests,quota exceeded,insufficient_quota,You've hit your usage limit` |
| `claude_retry_patterns` | Transient claude/fya markers retried like executor timeouts (comma-separated) | `FYA_TRANSIENT_TIMEOUT,API Error: 529,API Error: 502,API Error: 503,API Error: 504` |
| `wait_on_limit` | Wait duration before retrying on rate limit (e.g., `1h`, `30m`) | disabled |
Expand Down
2 changes: 1 addition & 1 deletion llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ Requirements: codex CLI ≥ 0.130.0. Older versions silently ignore unknown `-c`

**Transient retry:** `claude_retry_patterns` config option detects transient claude/fya markers and retries them through the existing timeout-style phase path. Default: `FYA_TRANSIENT_TIMEOUT,API Error: 529,API Error: 502,API Error: 503,API Error: 504`. The transient HTTP errors (529 Overloaded and the 502/503/504 gateway errors) are auto-retried here rather than gated behind `--wait`, since they are short-lived server hiccups, not account-quota limits. Retry patterns are checked before limit and error patterns and do not use `wait_on_limit`. The task and review retry loops wait a short fixed backoff (5s) before re-running a timed-out or transiently-failed iteration.

**Rate limit retry:** `--wait` flag (or `wait_on_limit` config option) enables automatic retry when rate limits are detected. Limit patterns (`claude_limit_patterns`, `codex_limit_patterns`) are checked before error patterns — when a limit pattern matches and wait is configured, ralphex waits the specified duration and retries. Without `--wait`, limit matches fall through to error pattern behavior (exit). Default limit patterns: `You've hit your limit,You've hit your session limit,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit` (claude), `Rate limit exceeded,rate limit reached,429 Too Many Requests,quota exceeded,insufficient_quota,You've hit your usage limit` (codex). The transient HTTP errors `API Error: 529/502/503/504` are no longer in the claude limit set — they moved to `claude_retry_patterns` so they auto-retry without `--wait`. The codex defaults are tightened so that review findings that *talk about* rate limiting in a codebase do not trip a false positive. Users who customized `codex_limit_patterns` or `codex_error_patterns` to an earlier default (e.g. `Rate limit,quota exceeded` or `Rate limit,quota exceeded,You've hit your usage limit`) keep their customization on update — comment the line out to inherit the new embedded default. Pattern matching scans both stdout and stderr (live, untruncated) so detection survives the 5-line / 256-rune error-context tail.
**Rate limit retry:** `--wait` flag (or `wait_on_limit` config option) enables automatic retry when rate limits are detected. Limit patterns (`claude_limit_patterns`, `codex_limit_patterns`) are checked before error patterns — when a limit pattern matches and wait is configured, ralphex waits the specified duration and retries. Without `--wait`, limit matches fall through to error pattern behavior (exit). Default limit patterns: `You've hit your limit,You've hit your session limit,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit,You've hit your individual spend limit` (claude), `Rate limit exceeded,rate limit reached,429 Too Many Requests,quota exceeded,insufficient_quota,You've hit your usage limit` (codex). The transient HTTP errors `API Error: 529/502/503/504` are no longer in the claude limit set — they moved to `claude_retry_patterns` so they auto-retry without `--wait`. The codex defaults are tightened so that review findings that *talk about* rate limiting in a codebase do not trip a false positive. Users who customized `codex_limit_patterns` or `codex_error_patterns` to an earlier default (e.g. `Rate limit,quota exceeded` or `Rate limit,quota exceeded,You've hit your usage limit`) keep their customization on update — comment the line out to inherit the new embedded default. Pattern matching scans both stdout and stderr (live, untruncated) so detection survives the 5-line / 256-rune error-context tail.

**Plan move behavior:** `move_plan_on_completion` config option controls whether completed plans move to `docs/plans/completed/` on success. Default `true` (existing behavior). Set to `false` for workflows that manage plan file lifecycle externally, such as spec-driven tooling with separate archive steps.

Expand Down
8 changes: 4 additions & 4 deletions pkg/config/defaults/config
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,8 @@ vcs_command = git
# API Error codes are enumerated rather than a bare "API Error:" so case-insensitive
# matching does not abort a successful run on narrated "API error:" prose (e.g. a ticket
# title); transient 529/502/503/504 live in claude_retry_patterns
# default: You've hit your limit,You've hit your session limit,API Error: 400,API Error: 401,API Error: 403,API Error: 404,API Error: 413,API Error: 429,API Error: 500,cannot be launched inside another Claude Code session,Not logged in,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit
claude_error_patterns = You've hit your limit,You've hit your session limit,API Error: 400,API Error: 401,API Error: 403,API Error: 404,API Error: 413,API Error: 429,API Error: 500,cannot be launched inside another Claude Code session,Not logged in,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit
# default: You've hit your limit,You've hit your session limit,API Error: 400,API Error: 401,API Error: 403,API Error: 404,API Error: 413,API Error: 429,API Error: 500,cannot be launched inside another Claude Code session,Not logged in,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit,You've hit your individual spend limit
claude_error_patterns = You've hit your limit,You've hit your session limit,API Error: 400,API Error: 401,API Error: 403,API Error: 404,API Error: 413,API Error: 429,API Error: 500,cannot be launched inside another Claude Code session,Not logged in,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit,You've hit your individual spend limit

# codex_error_patterns: patterns to detect in codex output indicating errors
# comma-separated list of substrings (case-insensitive matching)
Expand All @@ -279,8 +279,8 @@ claude_retry_patterns = FYA_TRANSIENT_TIMEOUT,API Error: 529,API Error: 502,API
# when detected AND wait_on_limit is set, ralphex waits and retries
# when detected WITHOUT wait_on_limit, falls through to error pattern behavior
# transient HTTP errors (529/502/503/504) live in claude_retry_patterns, not here
# default: You've hit your limit,You've hit your session limit,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit
claude_limit_patterns = You've hit your limit,You've hit your session limit,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit
# default: You've hit your limit,You've hit your session limit,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit,You've hit your individual spend limit
claude_limit_patterns = You've hit your limit,You've hit your session limit,Your usage allocation has been disabled by your admin,You've hit your org's monthly usage limit,You've hit your individual spend limit

# codex_limit_patterns: patterns to detect rate limits in codex/external output
# comma-separated list of substrings (case-insensitive matching)
Expand Down
4 changes: 2 additions & 2 deletions pkg/config/values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ func TestValuesLoader_Load_EmbeddedOnly(t *testing.T) {
assert.Equal(t, "docs/plans", values.PlansDir)
assert.Equal(t, "git", values.VcsCommand)
assert.Empty(t, values.CommitTrailer)
assert.Equal(t, []string{"You've hit your limit", "You've hit your session limit", "API Error: 400", "API Error: 401", "API Error: 403", "API Error: 404", "API Error: 413", "API Error: 429", "API Error: 500", "cannot be launched inside another Claude Code session", "Not logged in", "Your usage allocation has been disabled by your admin", "You've hit your org's monthly usage limit"}, values.ClaudeErrorPatterns)
assert.Equal(t, []string{"You've hit your limit", "You've hit your session limit", "API Error: 400", "API Error: 401", "API Error: 403", "API Error: 404", "API Error: 413", "API Error: 429", "API Error: 500", "cannot be launched inside another Claude Code session", "Not logged in", "Your usage allocation has been disabled by your admin", "You've hit your org's monthly usage limit", "You've hit your individual spend limit"}, values.ClaudeErrorPatterns)
assert.Equal(t, []string{"Rate limit exceeded", "rate limit reached", "429 Too Many Requests", "quota exceeded", "insufficient_quota", "You've hit your usage limit"}, values.CodexErrorPatterns)
assert.Equal(t, []string{"You've hit your limit", "You've hit your session limit", "Your usage allocation has been disabled by your admin", "You've hit your org's monthly usage limit"}, values.ClaudeLimitPatterns)
assert.Equal(t, []string{"You've hit your limit", "You've hit your session limit", "Your usage allocation has been disabled by your admin", "You've hit your org's monthly usage limit", "You've hit your individual spend limit"}, values.ClaudeLimitPatterns)
assert.Equal(t, []string{"Rate limit exceeded", "rate limit reached", "429 Too Many Requests", "quota exceeded", "insufficient_quota", "You've hit your usage limit"}, values.CodexLimitPatterns)
assert.Equal(t, []string{"FYA_TRANSIENT_TIMEOUT", "API Error: 529", "API Error: 502", "API Error: 503", "API Error: 504"}, values.ClaudeRetryPatterns)
assert.Zero(t, values.WaitOnLimit)
Expand Down
7 changes: 7 additions & 0 deletions pkg/executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,13 @@ func TestMatchPattern(t *testing.T) {
patterns: enumeratedAPIErrorCodes, want: "API Error: 500"},
{name: "genuine api error 401 matched by enumerated code",
output: "API Error: 401 Unauthorized", patterns: enumeratedAPIErrorCodes, want: "API Error: 401"},
// #425: "individual spend" sits between "your" and "limit", so the older entries cannot cover this wording
{name: "individual spend limit missed by older limit patterns",
output: "You've hit your individual spend limit · run /usage-credits to raise it, or visit claude.ai/admin-settings/usage",
patterns: []string{"You've hit your limit", "You've hit your session limit"}, want: ""},
{name: "individual spend limit matched by its own pattern",
output: "You've hit your individual spend limit · run /usage-credits to raise it, or visit claude.ai/admin-settings/usage",
patterns: []string{"You've hit your individual spend limit"}, want: "You've hit your individual spend limit"},
}

for _, tc := range tests {
Expand Down