Hmbown/CodeWhale · error

attempt finalization status must be terminal

Error message

attempt finalization status must be terminal

What it means

FleetLedger::finalize_task_attempt_if_leased accepts an optional final_status that is written into the TaskAttemptFinalized record. If final_status is Some but is not a terminal ledger status (Completed/Failed/Cancelled), the guard at crates/tui/src/fleet/ledger.rs:975 rejects the call: finalizing an attempt into a non-terminal status (e.g. Leased, Queued) would corrupt the ledger state machine. Passing None is allowed (keep the event-derived status).

Solutions

  1. Pass final_status = None to keep the status derived from the terminal event itself.
  2. Set final_status only to FleetTaskLedgerStatus::Completed, Failed, or Cancelled.
  3. Fix the status-mapping function so it produces a terminal variant for finalize calls.

Example fix

// before
let final_status = Some(FleetTaskLedgerStatus::Leased);
ledger.finalize_task_attempt_if_leased(&run_id, &w, &t, attempts, &ts, payload, final_status, receipt)?;
// after
let final_status = Some(match outcome {
    Outcome::Done => FleetTaskLedgerStatus::Completed,
    Outcome::Err => FleetTaskLedgerStatus::Failed,
    Outcome::Stopped => FleetTaskLedgerStatus::Cancelled,
});
ledger.finalize_task_attempt_if_leased(&run_id, &w, &t, attempts, &ts, payload, final_status, receipt)?;
Defensive patterns

Strategy: validation

Validate before calling

if let Some(s) = final_status {
    assert!(matches!(s, FleetTaskLedgerStatus::Completed | FleetTaskLedgerStatus::Failed | FleetTaskLedgerStatus::Cancelled));
}

Type guard

fn is_terminal_status(s: FleetTaskLedgerStatus) -> bool { matches!(s, FleetTaskLedgerStatus::Completed | FleetTaskLedgerStatus::Failed | FleetTaskLedgerStatus::Cancelled) }

Try / catch

if let Err(e) = finalize_result {
    if e.to_string().contains("status must be terminal") { /* fix the status mapping, non-retryable */ }
}

Prevention

When it happens

Trigger: Calling finalize_task_attempt_if_leased with final_status set to a non-terminal value such as FleetTaskLedgerStatus::Leased, Queued, or Running; mapping a worker outcome to the wrong status variant; a stale enum copied from a progress-update path.

Common situations: Status-mapping tables written before the terminal-only rule; refactors that renamed or re-ordered FleetTaskLedgerStatus variants; supervisor code that finalizes with the task's current status instead of the terminal outcome.

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


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/88623235c7d92463. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/fleet/ledger.rs:975

        mut receipt: FleetReceipt,
    ) -> Result<Option<FleetWorkerEvent>> {
        if !matches!(
            &payload,
            FleetWorkerEventPayload::Completed { .. }
                | FleetWorkerEventPayload::Failed { .. }
                | FleetWorkerEventPayload::Cancelled { .. }
        ) {
            bail!("attempt finalization requires a terminal worker event");
        }
        if final_status.is_some_and(|status| {
            !matches!(
                status,
                FleetTaskLedgerStatus::Completed
                    | FleetTaskLedgerStatus::Failed
                    | FleetTaskLedgerStatus::Cancelled
            )
        }) {
            bail!("attempt finalization status must be terminal");
        }
        if receipt.run_id != *run_id || receipt.task_id != task_id || receipt.worker_id != worker_id
        {
            bail!("attempt receipt identity does not match its terminal event");
        }
        if receipt
            .attempt
            .is_some_and(|attempt| attempt != expected_attempts)
        {
            bail!("attempt receipt generation does not match its lease");
        }
        self.with_write_lock(|| {
            let state = self.rebuild_state_unlocked()?;
            let key = task_key(&run_id.0, task_id);
            let Some(task) = state.tasks.get(&key) else {
                return Ok(None);
            };
            if task.status != FleetTaskLedgerStatus::Leased

View on GitHub (pinned to 73e0f67d83)