Hmbown/CodeWhale · error
Only / review passes completed; the partial review was not…
Error message
Only {}/{} review passes completed; the partial review was not accepted or posted. What it means
`ReviewAggregation::finish` guards the PR review pipeline against posting a partial review. Before merging per-pass outputs into a `ReviewOutput`, it asserts that the number of collected pass outputs equals the number of passes declared in the manifest; if any pass was skipped, failed, or returned early, the review is rejected instead of being accepted or posted with incomplete coverage.
Solutions
- Inspect the logs for the failed/skipped review pass and fix its root cause, then re-run the review
- Ensure every planned pass's output is pushed to the aggregator (including defensive pushes on error paths) before calling `finish`
- Re-run the whole review from scratch so all manifest passes complete
Example fix
// before
if outputs.len() < manifest.passes.len() { finish(outputs.as_slice(), &complete_diff)?; }
// after
anyhow::ensure!(outputs.len() == manifest.passes.len(), "missing review pass outputs");
let (review, diff, coverage) = finish(outputs, &complete_diff)?; Defensive patterns
Strategy: validation
Validate before calling
if aggregator.outputs.len() != aggregator.manifest.passes.len() { return Err(anyhow!("review incomplete: {}/{} passes", aggregator.outputs.len(), aggregator.manifest.passes.len())); } Try / catch
match aggregator.finish(&complete_diff) { Ok((review, diff, coverage)) => post(review), Err(e) => { log::error!("review rejected: {e}"); restart_full_review(); } } Prevention
- Push an output (or explicit failure marker) for every planned pass before calling finish
- Wrap the pass loop so a pass error aborts the whole review rather than silently skipping
- Add a debug assertion comparing outputs.len() and manifest.passes.len() in tests
When it happens
Trigger: Calling `finish(complete_diff)` on an aggregator whose `outputs` vec has fewer entries than `manifest.passes.len()` — e.g. a review pass errored, was skipped due to a missing diff for its slice, or an agent loop dropped one of the planned passes.
Common situations: A subagent pass timed out or panicked and its output was not pushed; a manifest was built for N passes but only N-1 succeeded and the code called `finish` anyway; partial-result handling drifted after a refactor of the pass loop.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Complete PR diff fingerprint changed before review…
- Review receipt does not cover every planned PR pass
- Cloud agent produced an empty patch; refusing to open a PR.
- Cloud agent produced no branch head to raise.
- Complete local diff requires
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/5c3199ec7578cd5b.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/review.rs:724
&& self.manifest.passes.get(expected - 1) == Some(&pass.manifest),
"Review pass arrived out of order or does not match the immutable manifest; expected pass {expected}"
);
let output = ReviewOutput::from_structured_str(&raw).ok_or_else(|| {
anyhow::anyhow!(
"Review pass {expected}/{} did not return valid structured JSON; the partial review was not accepted or posted.",
self.manifest.passes.len()
)
})?;
self.outputs.push(output);
self.raw_outputs.push(raw);
Ok(())
}
pub(crate) fn finish(
self,
complete_diff: &str,
) -> anyhow::Result<(ReviewOutput, String, ReviewReceiptCoverage)> {
anyhow::ensure!(
self.outputs.len() == self.manifest.passes.len(),
"Only {}/{} review passes completed; the partial review was not accepted or posted.",
self.outputs.len(),
self.manifest.passes.len()
);
anyhow::ensure!(
diff_fingerprint(complete_diff) == self.manifest.diff_fingerprint,
"Complete PR diff fingerprint changed before review aggregation"
);
let mut issues = Vec::new();
let mut suggestions = Vec::new();
let mut summaries = Vec::new();
let mut assessments = Vec::new();
for (index, output) in self.outputs.into_iter().enumerate() {
if !output.summary.is_empty() {
summaries.push(format!("Pass {}: {}", index + 1, output.summary));
}
if !output.overall_assessment.is_empty() {View on GitHub (pinned to 73e0f67d83)