fix(query): warn before repeated tool failures stop - #1927
Conversation
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 7 minutes Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe tool-failure loop guard now emits a near-threshold advisory, and ChangesTool failure advisory
Estimated code review effort: 3 (Moderate) | ~25 minutes Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/query/toolFailureLoopGuard.ts (1)
158-197: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winPreserve the advisory when a batch also contains success
hasSuccessreturns early with{ tripped: false }, so a near-threshold failure can drop its warning whenever the same batch includes an unrelated success. The persistent count stays atthreshold - 1, but the model never sees the “one more failure will stop the query” advisory. Return the advisory from this branch too.🐛 Proposed fix
if (hasSuccess) { resetToolFailureLoopGuard(params.state, successfulMutationPaths) - return { tripped: false } + return advisory ? { tripped: false, advisory } : { tripped: false } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/query/toolFailureLoopGuard.ts` around lines 158 - 197, Preserve any pending advisory when the batch contains a successful mutation: update the hasSuccess branch in the tool failure loop guard to reset state and return `{ tripped: false, advisory }` rather than discarding the advisory. Use the existing advisory variable and resetToolFailureLoopGuard logic without changing path-trip behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/query/toolFailureLoopGuard.test.ts`:
- Around line 94-153: Add a test alongside the existing tool failure loop guard
cases covering a single update containing one successful tool result and a
different tool’s matching failure that reaches threshold minus one. Assert the
decision is not tripped and includes the expected advisory for the failing tool,
including its tool name, error category, and remaining-failure message; use
update and the existing toolUse/toolResult helpers.
- Around line 854-871: Replace the source-text position assertions in the test
“query loop forwards an advisory to the next model turn” with a behavioral test
that invokes the query loop using mocked tool-failure/advisory decisions, then
verifies the advisory is forwarded as a meta message in the next model turn or
pushed tool result. Use the query generator’s existing test seams and mocks, and
ensure the scenario covers the success-handling path so advisory messages are
not dropped.
---
Outside diff comments:
In `@src/query/toolFailureLoopGuard.ts`:
- Around line 158-197: Preserve any pending advisory when the batch contains a
successful mutation: update the hasSuccess branch in the tool failure loop guard
to reset state and return `{ tripped: false, advisory }` rather than discarding
the advisory. Use the existing advisory variable and resetToolFailureLoopGuard
logic without changing path-trip behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 10065724-7312-4d36-8971-4ae9724b5f60
📒 Files selected for processing (3)
src/query.tssrc/query/toolFailureLoopGuard.test.tssrc/query/toolFailureLoopGuard.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
- GitHub Check: CodeRabbit / Review
- GitHub Check: smoke-and-tests (22)
- GitHub Check: smoke-and-tests (24.11.x)
- GitHub Check: typecheck
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
TypeScript code in this repository must use strict mode and ESM imports.
Files:
src/query.tssrc/query/toolFailureLoopGuard.test.tssrc/query/toolFailureLoopGuard.ts
**
⚙️ CodeRabbit configuration file
**: # AGENTS.md - AI Agent Coding GuideThis guide is for AI coding agents working in the OpenClaude repository. Read it before changing code, and also follow CONTRIBUTING.md for contributor policy, PR expectations, review follow-up, and project scope.
Project Snapshot
OpenClaude is a coding-agent CLI for cloud and local model providers. It supports OpenAI-compatible APIs, Anthropic, Gemini, DeepSeek, Ollama, MCP, local backends, slash commands, tools, agents, and a React/Ink terminal UI.
The installed CLI runs on Node.js
>=22.0.0. Bun is used for source builds, scripts, dependency management, and tests.Work Style
- Keep changes focused on one problem.
- Prefer existing patterns in the file or nearby module.
- Avoid unrelated formatting, renames, dependency changes, or broad rewrites.
- Add or update tests when behavior changes.
- Update docs when setup, commands, provider behavior, or user-facing behavior changes.
- For new features, larger refactors, dependencies, or runtime changes, follow the issue-first guidance in CONTRIBUTING.md.
Stack And Conventions
- TypeScript with strict mode and ESM imports.
- React + Ink for terminal UI.
- Bun lockfile and Bun scripts for development workflows.
- Node runtime for the built CLI.
Common libraries and patterns:
chalkfor terminal color.commanderfor CLI argument parsing.execafor child processes.- Existing service, provider, settings, permission, and UI patterns over new abstractions.
Repository Map
src/commands/- slash and CLI command implementations.src/components/- React/Ink UI components.src/services/- API, MCP, OAuth, wiki, voice, and other service integrations.src/tools/- tool implementations.src/utils/- shared utilities.src/integrations/- provider and model integration metadata.src/entrypoints/- CLI, MCP, SDK, and generated public types.src/tasks/- local, remote, workflow, and monitor tas...
Files:
src/query.tssrc/query/toolFailureLoopGuard.test.tssrc/query/toolFailureLoopGuard.ts
**/*
⚙️ CodeRabbit configuration file
**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.
Files:
src/query.tssrc/query/toolFailureLoopGuard.test.tssrc/query/toolFailureLoopGuard.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}
⚙️ CodeRabbit configuration file
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.
Files:
src/query/toolFailureLoopGuard.test.ts
🔇 Additional comments (2)
src/query/toolFailureLoopGuard.ts (1)
29-41: LGTM!Also applies to: 136-136, 239-239, 478-494
src/query.ts (1)
2806-2825: LGTM! Forwarding logic (meta message construction, yield,toolResultsappend, ordering before the recursive call) matches the guard's advisory contract.One minor drive-by:
hasToolName/hasErrorCategoryin the debug log andlogEventpayload are alwaystruesinceadvisory.toolName/advisory.errorCategoryare required strings — logging the actualtoolName/errorCategoryvalues would be more useful than a constant flag, though this is inconsequential sincelogEventis currently a no-op.
0xghost42
left a comment
There was a problem hiding this comment.
Nice addition — a heads-up turn before the hard stop gives the model a real chance to change tack, and gating it on threshold > 1 && persistentSignatureCount === threshold - 1 makes it a clean single-shot warning at the penultimate failure rather than a repeated nag. The message is specific and actionable (names the tool, the error category, and the exact count), which is the right call.
One thing I'd verify: the "don't warn when no next turn is possible" case from the summary. The advisory is generated purely from the signature count in evaluateToolFailureLoop, and in query.ts it's yielded and pushed to toolResults unconditionally in the for (const advisoryDecision of ... advisories ?? []) loop. I don't see the guard for "this is already the last permitted turn (maxTurns / maxTokens reached)" in this diff — if that suppression lives upstream of this loop, great; if not, there's a path where the model gets "one more matching failure will stop the query" on a turn where it can't actually act, which is a slightly confusing dead-end. Worth a test that asserts no advisory is emitted on the final allowed turn.
Minor: the debug log computes hasToolName=${advisoryDecision.toolName !== undefined} while the logEvent right below hardcodes hasToolName: true, hasErrorCategory: true. Since both fields are required on the advisory type they're always defined, so true is accurate — but the two log sites disagree on whether the value is dynamic, which will read as a bug to the next person. I'd make them consistent (drop the !== undefined in the debug line, or compute both).
|
hello @jatmn please address some comments from @0xghost42 |
af0bf85 to
5266ff8
Compare
* fix(query): warn before repeated tool failures stop * fix(query): preserve tool failure advisories * fix(query): forward all tool failure advisories * fix(query): harden tool failure advisories * fix(query): preserve tool failure advisories * fix(query): keep advisories one-shot * fix(query): avoid duplicate advisories after compaction * fix(query): compare advisory message IDs * fix(query): cover advisory forwarding edges * fix(query): retain advisories without tools
Summary
Fixes #1926
Root cause
The tool-failure loop guard only returned hard-stop decisions. Models could repeatedly retry the same failing tool without being told that the next matching failure would terminate the query.
Validation
bun test src/query/toolFailureLoopGuard.test.tsbun run buildbun run smokeLimitations
The advisory is intentionally limited to persistent signature failures.
Final reviewed SHA:
5266ff8aa7f268ae6e8fda17d83e5689befb4aceSummary by CodeRabbit
New Features
Bug Fixes