fix(conductor): crashed/killed task subprocess is retryable (#742) - #821
Conversation
…tryable (#742) A subprocess that crashed or was SIGTERMed before finalizing its run row leaves the run in RUNNING. _execute_task_subprocess returned that verbatim, so the batch recorded results[task_id]="RUNNING" as terminal — resume_batch only re-ran FAILED/BLOCKED, so the task was permanently skipped, and the stale active run made the next start_task_run raise "already has an active run". - On nonzero exit with the run still RUNNING, reconcile it to FAILED before returning (clears the stale active run; records a retryable result). - Treat "RUNNING" as retryable in resume_batch for any legacy batches that already recorded it.
|
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 |
Third-party review (opencode / GLM) — pre-PRVerdict: No blocking issues. Verified against
Posted from the automated issue-lifecycle review gate. |
ReviewSmall, well-scoped fix: 1. 2. 3. 4. Altitude (non-blocking observation) — This adds a second, structurally independent reconciliation mechanism: the new one is Nothing here contradicts the pre-PR opencode review's verdict for the core scoped case ( |
…#742 review) claude-review: fail_run -> tasks.update_status can raise InvalidTransitionError (not ValueError) when a checkpoint restore desynced the task status. The run row is already written to FAILED before the task transition, so the stale active run is cleared regardless; broaden the except to any Exception and log at debug so the reconcile returns FAILED cleanly and observably instead of propagating.
|
Thanks — triaged all four:
|
Follow-up reviewI reviewed the changes made since my previous pass (commit 223cd3f) against the four findings I raised earlier. Status:
New (minor) observation:
|
…view) Follow-up review: the previous test mocked fail_run wholesale, so it proved 'don't propagate' but not the ordering guarantee (run row committed to FAILED before the task transition raises). Now let fail_run run for real and make only tasks.update_status raise; assert the run row is FAILED and no stale active run remains.
|
Addressed the follow-up's two minor items in 77542c6:
Thanks for the thorough tracing. |
Follow-up review (commit 77542c6, current HEAD)Re-verified the current state of the branch against the four findings from my earlier passes — all are resolved and I traced each claim against the actual source rather than just the PR description:
Two things I checked that weren't explicitly raised before, both come back clean:
I wasn't able to execute the test suite in this environment (sandbox blocked the |
Closes #742
Problem
_execute_task_subprocessreturned the active run's status verbatim even when the child crashed / was SIGTERMed before finalizing its run row (stillRUNNING). The batch then recordedresults[task_id]="RUNNING"as terminal. Becauseresume_batchonly re-ranFAILED/BLOCKED/missing tasks, the task was permanently skipped, and the staleRUNNINGrun made the nextstart_task_runraise "already has an active run".Fix
_execute_task_subprocess: on nonzero exit with the run stillRUNNING, reconcile it toFAILEDviafail_runbefore returning. This clears the stale active run (so a later restart isn't blocked) and records a retryable result. Scoped toreturncode != 0, which covers both crash exits (positive) and signal kills (negative, e.g. -15 SIGTERM / -9 SIGKILL).resume_batch: add"RUNNING"to the retryable set so any legacy batches that already recorded it can be resumed.Acceptance criteria
Tests
test_subprocess_reconciles_stale_running_run_to_failed— real RUNNING run + mocked nonzero-exit Popen → assertsFAILEDreturned, no stale active run, run row is FAILED.test_resume_reruns_stale_running_task— batch that recordsRUNNING→ resume re-runs only that task and it completes.Full
tests/core/test_conductor.py(67) passes;ruffclean.Review
Pre-PR cross-family review via opencode (GLM): No blocking issues — confirmed
fail_run'sValueErrorcontract, thereturncode != 0scoping, and no double-transition/race.Known limitations
returncode == 0+ still-RUNNINGedge (child exited clean but never finalized) is a separate child-side bug, out of scope here (the criteria scope to nonzero exit). It is retryable via the resume change but costs one wasted resume cycle: the first retry hits the stale active run, self-heals it to FAILED on that retry's nonzero exit, and a second resume actually re-executes. Reconciling a clean-exit run to FAILED is deliberately avoided since it would risk misreporting a task that actually succeeded.