Hmbown/CodeWhale · error

attempt finalization requires a terminal worker event

Error message

attempt finalization requires a terminal worker event

What it means

FleetLedger::finalize_task_attempt_if_ledger finalizes one exact process attempt and its receipt in a single JSONL record. The first guard (crates/tui/src/fleet/ledger.rs:965) requires the payload passed to be a terminal worker event (Completed/Failed/Cancelled); finalizing an attempt on a non-terminal payload is rejected before any lock is taken. This keeps receipt evidence pinned to an actual terminal transition.

Solutions

  1. Pass only Completed, Failed, or Cancelled payloads to finalize_task_attempt_if_leased.
  2. Append progress events via append_event_if_lease_unchanged and reserve finalize_task_attempt_if_leased for the terminal transition.
  3. Select the payload based on the outcome status before constructing the finalize call.

Example fix

// before
let payload = FleetWorkerEventPayload::Progress { .. };
ledger.finalize_task_attempt_if_leased(&run_id, &w, &t, attempts, &ts, payload, Some(status), receipt)?;
// after
let payload = match status {
    FleetTaskLedgerStatus::Completed => FleetWorkerEventPayload::Completed { .. },
    FleetTaskLedgerStatus::Failed => FleetWorkerEventPayload::Failed { .. },
    _ => FleetWorkerEventPayload::Cancelled { .. },
};
ledger.finalize_task_attempt_if_leased(&run_id, &w, &t, attempts, &ts, payload, Some(status), receipt)?;
Defensive patterns

Strategy: validation

Validate before calling

assert!(is_terminal_payload(&payload), "finalize requires a terminal worker event");

Try / catch

if let Err(e) = ledger.finalize_task_attempt_if_leased(...) {
    if e.to_string().contains("requires a terminal worker event") { /* fix payload selection, do not retry blindly */ }
    return Err(e.into());
}

Prevention

When it happens

Trigger: Calling finalize_task_attempt_if_leased with a non-terminal FleetWorkerEventPayload (Progress, UsageReport, Heartbeat) — typically from a verifier or supervisor path that computes the receipt before selecting the terminal event variant.

Common situations: A worker wrapper that emits generic events into the finalize call; a restart-race handler that re-finalizes with a reused (non-terminal) payload; code shared between progress-append and finalize paths that forgot to branch.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

    #[allow(clippy::too_many_arguments)]
    pub fn finalize_task_attempt_if_leased(
        &self,
        run_id: &FleetRunId,
        worker_id: &str,
        task_id: &str,
        expected_attempts: u32,
        timestamp: &str,
        payload: FleetWorkerEventPayload,
        final_status: Option<FleetTaskLedgerStatus>,
        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)

View on GitHub (pinned to 73e0f67d83)