Hmbown/CodeWhale · error

conditional terminal append requires a terminal worker event

Error message

conditional terminal append requires a terminal worker event

What it means

FleetLedger::append_terminal_event_if_leased is the compare-and-set API for appending terminal worker events (Completed/Failed/Cancelled) while a lease is still live. The guard at crates/tui/src/fleet/ledger.rs:916 rejects any call whose payload is a non-terminal event (e.g. Progress, Heartbeat, UsageReport). It is an argument precondition, not a ledger-state problem: the method is terminal-only by contract.

Solutions

  1. Route non-terminal payloads through FleetLedger::append_event_if_leased or append_event_if_lease_unchanged instead.
  2. Map the payload to a terminal variant (Completed/Failed/Cancelled) before calling append_terminal_event_if_leased.
  3. Add a matches! assertion or match arm upstream so only terminal payloads reach this call site.

Example fix

// before
ledger.append_terminal_event_if_leased(&run_id, &worker_id, &task_id, attempts, &ts, payload)?;
// after
if is_terminal_payload(&payload) {
    ledger.append_terminal_event_if_leased(&run_id, &worker_id, &task_id, attempts, &ts, payload)?;
} else {
    ledger.append_event_if_leased(&run_id, &worker_id, &task_id, attempts, &ts, payload)?;
}
Defensive patterns

Strategy: validation

Validate before calling

fn is_terminal_payload(p: &FleetWorkerEventPayload) -> bool {
    matches!(p, FleetWorkerEventPayload::Completed { .. } | FleetWorkerEventPayload::Failed { .. } | FleetWorkerEventPayload::Cancelled { .. })
}
assert!(is_terminal_payload(&payload), "terminal append needs terminal payload");

Type guard

let is_terminal_payload = |p: &FleetWorkerEventPayload| matches!(p, FleetWorkerEventPayload::Completed { .. } | FleetWorkerEventPayload::Failed { .. } | FleetWorkerEventPayload::Cancelled { .. });

Try / catch

match ledger.append_terminal_event_if_leased(&run_id, &w, &t, attempts, &ts, payload) {
    Ok(Some(event)) => { /* terminal recorded */ }
    Ok(None) => { /* lease moved on */ }
    Err(e) if e.to_string().contains("requires a terminal worker event") => { /* dispatch to progress API */ }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling FleetLedger::append_terminal_event_if_leased with a non-terminal FleetWorkerEventPayload such as Progress, UsageReport, or Heartbeat — usually because the caller picked the wrong variant of the conditional-append family (the progress-only variants are append_event_if_leased / append_event_if_lease_unchanged).

Common situations: Refactors that unify progress and completion paths into one helper and pass the payload straight through; writing a new scheduler/worker loop that reuses the terminal API for intermediate updates; misremembering which conditional-append method owns which payload class.

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/92861a9c4c4b95e7. Report an issue: GitHub.

Appendix: source

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

    /// still live. This is the completion side of the same compare-and-set used
    /// by cancellation: whichever terminal transition acquires the ledger lock
    /// first wins, and the loser cannot overwrite the task or mint a receipt.
    pub fn append_terminal_event_if_leased(
        &self,
        run_id: &FleetRunId,
        worker_id: &str,
        task_id: &str,
        expected_attempts: u32,
        timestamp: &str,
        payload: FleetWorkerEventPayload,
    ) -> Result<Option<FleetWorkerEvent>> {
        if !matches!(
            &payload,
            FleetWorkerEventPayload::Completed { .. }
                | FleetWorkerEventPayload::Failed { .. }
                | FleetWorkerEventPayload::Cancelled { .. }
        ) {
            bail!("conditional terminal append requires a terminal worker event");
        }
        let appended = 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
                || task.leased_to.as_deref() != Some(worker_id)
                || task.entry.attempts != expected_attempts
            {
                return Ok(None);
            }
            let event = next_worker_event(&state, run_id, worker_id, task_id, timestamp, payload);
            self.append_record_unlocked(&FleetLedgerRecord::EventAppended {
                event: event.clone(),
            })?;
            Ok(Some(event))

View on GitHub (pinned to 73e0f67d83)