pbakaus/impeccable · error
build-phase: finish --disposition ship refused: {} {} not cl
Error message
build-phase: finish --disposition ship refused: {} {} not closed (phase {phase}). Record fix or rebuild, or close the phases first; a page shipped over an open hero is a page shipped against its own gate.
What it means
The `build-phase finish --disposition ship` command refuses to mark a build phase as shipped while there are still open (unclosed) phase items. The build-phase subsystem acts as a gate: shipping a page over an open hero/phase would mean shipping against the project's own quality gate. The error names each open item, the singular/plural verb, and the current phase so the developer knows exactly what to close first.
Source
Thrown at crates/comp-verbs/src/build_phase.rs:2016
io.err("build-phase: finish --disposition ship|fix|rebuild|recapture\n");
return 1;
}
let disposition = disposition.unwrap();
let open_before: Vec<String> = PHASES
.iter()
.filter(|&&ph| {
ph != "review"
&& state
.pointer(&format!("/phases/{ph}/status"))
.and_then(Value::as_str)
.map(|st| st != "closed" && st != "skipped")
.unwrap_or(false)
})
.map(|s| s.to_string())
.collect();
if disposition == "ship" && !open_before.is_empty() {
let phase = state.get("phase").and_then(Value::as_str).unwrap_or("");
io.err(&format!(
"build-phase: finish --disposition ship refused: {} {} not closed (phase {phase}). Record fix or rebuild, or close the phases first; a page shipped over an open hero is a page shipped against its own gate.\n",
open_before.join(", "),
if open_before.len() == 1 { "is" } else { "are" }
));
return 2;
}
let phase = state.get("phase").and_then(Value::as_str).unwrap_or("").to_string();
state.as_object_mut().unwrap().insert("finish".into(), json!({ "disposition": disposition, "at": now(), "phaseAtFinish": phase }));
if phase == "review" {
if let Some(rev) = state.pointer_mut("/phases/review").and_then(|v| v.as_object_mut()) {
rev.insert("status".into(), json!("closed"));
rev.insert("closedAt".into(), json!(now()));
}
}
save_state(io, &state);
io.out(&format!("{}\n", render_status(io, &state)));
0
}View on GitHub (pinned to 2bc2879276)
Solutions
- Run the build-phase status output to list the open items named in the error.
- Close each open phase by recording its fix or rebuild outcome (e.g. `build-phase finish --disposition fix` / `rebuild` per item) before re-running with `--disposition ship`.
- If the open items are stale/obsolete, close them explicitly rather than deleting state, then ship.
- Re-run `build-phase finish --disposition ship` once `open_before` is empty.
Example fix
// before $ impeccable build-phase finish --disposition ship build-phase: finish --disposition ship refused: hero is not closed (phase build). ... // after $ impeccable build-phase record hero --outcome fix $ impeccable build-phase finish --disposition ship
Defensive patterns
Strategy: validation
Validate before calling
const status = JSON.parse(runSync('impeccable build-phase status').stdout);
if (status.open && status.open.length > 0) {
throw new Error(`Cannot ship: open phases: ${status.open.join(', ')}. Close them first.`);
} Type guard
function canShip(state) {
return Array.isArray(state?.open) && state.open.length === 0;
} Try / catch
try {
runSync('impeccable build-phase finish --disposition ship');
} catch (e) {
if (e.stderr?.includes('not closed')) {
console.error('Open phases remain; record fix/rebuild for each item before shipping.');
process.exit(2);
}
throw e;
} Prevention
- Always run build-phase status immediately before a ship disposition.
- Automate close-out: record fix/rebuild outcomes as part of the fix workflow, not at the end.
- Never hand-edit the phase state file; use the CLI so open items stay consistent.
- Treat exit code 2 from finish as 'gate blocked', and stop the pipeline.
When it happens
Trigger: Running `impeccable build-phase finish --disposition ship` while `state.open` (open_before) is non-empty; i.e. phase items recorded as 'fix' or 'rebuild' have not been closed via a subsequent finish/record operation before attempting ship.
Common situations: Developers finishing a build session forget to record fixes or rebuild outcomes for flagged items; multiple phases were opened but only some were closed; the phase state file (.impeccable build-phase state) was edited or restored from an older snapshot leaving stale open items.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- build-phase: unknown command {other}
- concept-seed: --candidate-count must be an integer from 5 to
- Unknown ignore-rule flag: ${arg}
- Pass a rule id, e.g. ${IMPECCABLE_COMMAND} hooks ignore-rule
- overused-font is value-specific by default. Use ${IMPECCABLE
AI-assisted analysis of pbakaus/impeccable@2bc2879276 (2026-09-08).
Data as JSON: /api/errors/234e67a4f2cd83bb.
Report an issue: GitHub.