{"record":{"id":"ddeca9ee332c9961","repo":"Hmbown/CodeWhale","slug":"lane-exit-receipt-belongs-to-expected","errorCode":null,"errorMessage":"lane exit receipt {} belongs to {}, expected {}","messagePattern":"lane exit receipt (.+?) belongs to (.+?), expected (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/lane/src/runtime.rs","lineNumber":534,"sourceCode":"fn read_lane_exit_receipt(log_path: &Path, lane_id: &str) -> Result<Option<LaneExitReceipt>> {\n    let path = lane_exit_receipt_path(log_path);\n    let metadata = match std::fs::metadata(&path) {\n        Ok(metadata) => metadata,\n        Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None),\n        Err(err) => return Err(err).with_context(|| format!(\"stat {}\", path.display())),\n    };\n    if metadata.len() > MAX_EXIT_RECEIPT_BYTES {\n        bail!(\n            \"lane exit receipt {} exceeds {} bytes\",\n            path.display(),\n            MAX_EXIT_RECEIPT_BYTES\n        );\n    }\n    let bytes = std::fs::read(&path).with_context(|| format!(\"read {}\", path.display()))?;\n    let receipt: LaneExitReceipt =\n        serde_json::from_slice(&bytes).with_context(|| format!(\"parse {}\", path.display()))?;\n    if receipt.lane_id != lane_id {\n        bail!(\n            \"lane exit receipt {} belongs to {}, expected {}\",\n            path.display(),\n            receipt.lane_id,\n            lane_id\n        );\n    }\n    Ok(Some(receipt))\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum TmuxSessionState {\n    Present,\n    Absent,\n}\n\nfn tmux_command(socket: &Path) -> Command {\n    let mut command = Command::new(\"tmux\");\n    command.arg(\"-S\").arg(socket);","sourceCodeStart":516,"sourceCodeEnd":552,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/lane/src/runtime.rs#L516-L552","documentation":"After parsing, read_lane_exit_receipt checks receipt.lane_id against the lane id it was asked about and bails on mismatch. The receipt is only meaningful for the lane that wrote it; a mismatch means the file at lane_exit_receipt_path(log_path) is stale — left by a previous lane that used the same log path. Acting on it would attribute another lane's exit code to this lane.","triggerScenarios":"Calling reconciliation for lane B while the receipt file under B's log_path still contains {\"lane_id\": \"A\", ...}. Happens when log paths are reused across lanes or generations and the old receipt was not removed.","commonSituations":"Regenerating lanes in a fixed directory (e.g. logs/lane-0) without cleanup; copying/cloning a workspace with leftover lane state; a lane id scheme that collides after truncation.","solutions":["Remove the stale receipt file named in the message, then reconcile again","Give each lane generation a unique log path (embed the lane id or a run id)","If you manage lifecycle code, clear receipt files when a lane's log directory is recycled"],"exampleFix":"// before\nlet log_path = logs_dir.join(\"lane-current.jsonl\"); // reused across lanes\n\n// after\nlet log_path = logs_dir.join(format!(\"{lane_id}.jsonl\")); // unique per lane","handlingStrategy":"fallback","validationCode":"// Use unique log paths per lane so receipts can never be cross-read.\nlet log_path = logs_dir.join(format!(\"{lane_id}.jsonl\"));","typeGuard":null,"tryCatchPattern":"match read_lane_exit_receipt(&log_path, &lane_id) {\n    Ok(Some(receipt)) => { /* exit code for this lane */ }\n    Err(err) if err.to_string().contains(\"belongs to\") => {\n        // Stale receipt from another lane: discard and fall back to tmux state.\n        let _ = std::fs::remove_file(lane_exit_receipt_path(&log_path));\n    }\n    other => other?,\n}","preventionTips":["Embed the lane id (or a unique run id) in every lane's log path","Clear receipt files when recycling a lane's log directory","Never copy lane state directories between lanes"],"tags":["rust","lane","exit-receipt","stale-state","validation"],"backgroundTag":"stale-state-file","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}