fix(api): fail_run on background agent-launch error, not stuck IN_PROGRESS (#722) - #794
Conversation
…GRESS (#722) POST /tasks/{id}/start?execute=true creates the run (task -> IN_PROGRESS) then runs execute_agent in a daemon thread whose except only logged + emitted an SSE error. A common misconfig (missing ANTHROPIC_API_KEY / unknown provider) raises ValueError before execute_agent's own try, so the run stayed RUNNING forever — every retry 400'd with 'already has an active run', and with no SSE client the error was lost. The background except now calls runtime.fail_run(workspace, run.id) first (guarded against a double-fail), resetting the task to FAILED (retryable). Closes #722
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 15 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: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Code ReviewSummary: Clean, targeted fix for a genuine P0 correctness bug — a background agent launch failure would leave the task permanently stuck in What's good
IssuesMinor — weak assertion in assert r2.status_code != 400This passes on 404, 500, or any other error response. The intent is that a retry is accepted, so the assertion should be: assert r2.status_code == 200Minor — comment block exceeds CLAUDE.md's single-line guideline CLAUDE.md says: "Only add one when the WHY is non-obvious … one short line max". The 5-line comment here is genuinely warranted (the invariant is subtle), but could be condensed without losing meaning: # Outer catch only: execute_agent handles its own errors; pre-execute raises
# (e.g. missing API key) reach here. Guarded against double-fail (#722).Nit
logger.debug("fail_run skipped for task (run not active)", task_id)OverallThe core fix is correct and the test coverage is solid. The |
What & why
POST /tasks/{id}/start?execute=truecreates the run (task → IN_PROGRESS), returns 200, then runsexecute_agentin a daemon thread. That thread'sexceptonly logged + emitted an SSEErrorEvent— it never reset the run. A very common misconfig (missingANTHROPIC_API_KEY, unknown provider) raisesValueErrorbeforeexecute_agent's own try, so the run stayedRUNNINGforever: every retry 400'd with "Task already has an active run", and if no SSE client was attached the error was lost entirely.Fixes #722 [P0.11].
Change
The background
exceptnow callsruntime.fail_run(workspace, run.id, reason=str(exc))before publishing the SSE error, resetting the task toFAILED(a retryable state). Guarded in its owntry/exceptso an already-FAILED run (e.g. an errorexecute_agenthandled internally) can't break the handler.Tests (
tests/ui/test_tasks_start_failure.py)With
execute_agentpatched to raise up front:start?execute=truereturns 200, the task ends FAILED not IN_PROGRESS, and a second start does not 400. Uses the real daemon thread + a bounded poll (a launch that fails up front resolves in ms) — an inline/synchronous thread would deadlock againststart_task_run's open SQLite write, which the real separate-thread flow avoids.75 passedacross tasks_v2 integration + this suite.ruff+mypyclean.Demo
Acceptance criteria
exceptcallsruntime.fail_run(...)before publishing the SSE error