{"record":{"id":"0e3003673e4b3c65","repo":"Hmbown/CodeWhale","slug":"attempt-receipt-generation-does-not-match-its-lease","errorCode":null,"errorMessage":"attempt receipt generation does not match its lease","messagePattern":"attempt receipt generation does not match its lease","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/ledger.rs","lineNumber":985,"sourceCode":"        if final_status.is_some_and(|status| {\n            !matches!(\n                status,\n                FleetTaskLedgerStatus::Completed\n                    | FleetTaskLedgerStatus::Failed\n                    | FleetTaskLedgerStatus::Cancelled\n            )\n        }) {\n            bail!(\"attempt finalization status must be terminal\");\n        }\n        if receipt.run_id != *run_id || receipt.task_id != task_id || receipt.worker_id != worker_id\n        {\n            bail!(\"attempt receipt identity does not match its terminal event\");\n        }\n        if receipt\n            .attempt\n            .is_some_and(|attempt| attempt != expected_attempts)\n        {\n            bail!(\"attempt receipt generation does not match its lease\");\n        }\n        self.with_write_lock(|| {\n            let state = self.rebuild_state_unlocked()?;\n            let key = task_key(&run_id.0, task_id);\n            let Some(task) = state.tasks.get(&key) else {\n                return Ok(None);\n            };\n            if task.status != FleetTaskLedgerStatus::Leased\n                || task.leased_to.as_deref() != Some(worker_id)\n                || task.entry.attempts != expected_attempts\n            {\n                return Ok(None);\n            }\n            let event = next_worker_event(&state, run_id, worker_id, task_id, timestamp, payload);\n            receipt.attempt = Some(expected_attempts);\n            receipt.terminal_seq = Some(event.seq);\n            self.append_record_unlocked(&FleetLedgerRecord::TaskAttemptFinalized {\n                event: event.clone(),","sourceCodeStart":967,"sourceCodeEnd":1003,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/fleet/ledger.rs#L967-L1003","documentation":"FleetLedger::finalize_task_attempt_if_leased stamps the receipt with the lease's attempt generation (expected_attempts). The guard at crates/tui/src/fleet/ledger.rs:985 rejects the call when the receipt carries an attempt number that differs from the current lease generation. Because each restart increments attempts, this blocks a late process or verifier from terminalizing or publishing evidence for an attempt that was already superseded.","triggerScenarios":"Finalizing with a receipt whose attempt field is Some(n) where n != expected_attempts — typically a receipt persisted by attempt N being replayed after the task was restarted and is now on attempt N+1.","commonSituations":"A crashed worker's verifier wakes up after a supervisor restarted the task; replaying queued receipts after a manager restart; stale receipts read from a checkpoint before a restart bumped attempts.","solutions":["Drop or regenerate the stale receipt and rebuild it for the current attempt generation before finalizing.","Verify receipt.attempt == expected_attempts before calling finalize_task_attempt_if_leased.","Skip finalization when the lease generation moved on — the replacement attempt owns the terminal transition now."],"exampleFix":"// before\nlet receipt = saved_receipt; // attempt 1\nledger.finalize_task_attempt_if_leased(&run_id, &w, &t, current_attempts, &ts, payload, status, receipt)?;\n// after\nlet receipt = if saved_receipt.attempt == Some(current_attempts) {\n    saved_receipt\n} else {\n    build_receipt_for_current_attempt(&run_id, w, t, current_attempts)\n};\nledger.finalize_task_attempt_if_leased(&run_id, &w, &t, current_attempts, &ts, payload, status, receipt)?;","handlingStrategy":"validation","validationCode":"if receipt.attempt.is_some_and(|a| a != expected_attempts) {\n    // regenerate receipt for the current attempt generation before finalizing\n}","typeGuard":"fn receipt_matches_lease(receipt: &FleetReceipt, expected_attempts: u32) -> bool { receipt.attempt.map_or(true, |a| a == expected_attempts) }","tryCatchPattern":"match ledger.finalize_task_attempt_if_leased(...) {\n    Ok(Some(_)) => { /* finalized */ }\n    Ok(None) => { /* lease superseded */ }\n    Err(e) if e.to_string().contains(\"generation does not match its lease\") => { /* drop stale receipt */ }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Invalidate stored receipts whenever a task restart increments attempts.","Check the lease generation from freshly rebuilt ledger state, not a cached snapshot.","Design verifiers/supervisors to no-op when their attempt generation is no longer current."],"tags":["fleet","ledger","stale-lease"],"backgroundTag":"invalid-state-transition","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}