Hmbown/CodeWhale · error

attempt receipt identity does not match its terminal event

Error message

attempt receipt identity does not match its terminal event

What it means

FleetLedger::finalize_task_attempt_if_leased binds the receipt to the terminal event it accompanies. The guard at crates/tui/src/fleet/ledger.rs:979 rejects the call when the receipt's run_id, task_id, or worker_id does not match the run/task/worker arguments of the call. A mismatched receipt would attach evidence from one attempt (or another task entirely) to a different attempt's terminal record.

Solutions

  1. Rebuild the FleetReceipt from the current attempt's run_id/task_id/worker_id before finalizing.
  2. Check the receipt fields against the call arguments and regenerate it when they diverge.
  3. Key receipt storage by (run_id, task_id, worker_id) so the wrong receipt cannot be fetched.

Example fix

// before
let receipt = old_receipt; // built for another attempt
ledger.finalize_task_attempt_if_leased(&run_id, &worker_id, &task_id, attempts, &ts, payload, status, receipt)?;
// after
assert_eq!(old_receipt.run_id, *run_id);
assert_eq!(old_receipt.task_id, task_id);
assert_eq!(old_receipt.worker_id, worker_id);
ledger.finalize_task_attempt_if_leased(&run_id, &worker_id, &task_id, attempts, &ts, payload, status, old_receipt)?;
Defensive patterns

Strategy: validation

Validate before calling

assert_eq!(receipt.run_id, *run_id);
assert_eq!(receipt.task_id, task_id);
assert_eq!(receipt.worker_id, worker_id);

Try / catch

if let Err(e) = finalize_result {
    if e.to_string().contains("receipt identity does not match") { /* rebuild receipt for this attempt */ }
}

Prevention

When it happens

Trigger: Calling finalize_task_attempt_if_leased with a FleetReceipt built for a different run, task, or worker — e.g. a cached receipt from a previous attempt, a receipt copied across workers, or arguments swapped at the call site.

Common situations: Reusing a stored receipt after the task was restarted on another worker; aggregating receipts in a map keyed by the wrong id; a driver loop that holds a stale receipt while task/worker identifiers were rebound.

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

Appendix: source

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

            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
                || task.leased_to.as_deref() != Some(worker_id)
                || task.entry.attempts != expected_attempts
            {
                return Ok(None);

View on GitHub (pinned to 73e0f67d83)