{"record":{"id":"3c007f350d076273","repo":"Hmbown/CodeWhale","slug":"serialized-lane-exit-receipt-exceeds-size-bound","errorCode":null,"errorMessage":"serialized lane exit receipt exceeds size bound","messagePattern":"serialized lane exit receipt exceeds size bound","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/lane/src/runtime.rs","lineNumber":378,"sourceCode":"            bail!(\"invalid lane environment key {key:?}\");\n        }\n    }\n    Ok(environment)\n}\n\nfn write_lane_exit_receipt(\n    receipt_path: &Path,\n    receipt_tmp_path: &Path,\n    lane_id: &str,\n    exit_code: i32,\n) -> Result<()> {\n    let encoded = serde_json::to_vec(&LaneExitReceipt {\n        lane_id: lane_id.to_string(),\n        exit_code,\n    })\n    .context(\"serialize lane exit receipt\")?;\n    if encoded.len() as u64 > MAX_EXIT_RECEIPT_BYTES {\n        bail!(\"serialized lane exit receipt exceeds size bound\");\n    }\n    remove_file_if_present(receipt_tmp_path)?;\n    let mut options = OpenOptions::new();\n    options.create_new(true).write(true);\n    #[cfg(unix)]\n    {\n        use std::os::unix::fs::OpenOptionsExt;\n        options.mode(0o600);\n    }\n    let result = (|| {\n        let mut file = options\n            .open(receipt_tmp_path)\n            .with_context(|| format!(\"create {}\", receipt_tmp_path.display()))?;\n        file.write_all(&encoded)\n            .with_context(|| format!(\"write {}\", receipt_tmp_path.display()))?;\n        file.sync_all()\n            .with_context(|| format!(\"sync {}\", receipt_tmp_path.display()))?;\n        fs::rename(receipt_tmp_path, receipt_path)","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/lane/src/runtime.rs#L360-L396","documentation":"write_lane_exit_receipt serializes the LaneExitReceipt (lane id plus exit code) and refuses to write if the JSON exceeds MAX_EXIT_RECEIPT_BYTES. The receipt is a fixed-shape tiny document, so exceeding the bound signals an internal invariant violation rather than user input.","triggerScenarios":"run_lane_log_proxy writing an exit receipt whose serialized form exceeds MAX_EXIT_RECEIPT_BYTES — realistically only if lane_id is enormous or the struct/serializer changed.","commonSituations":"Lane ids built from unbounded user input; a code change adding fields to LaneExitReceipt without revisiting the byte bound.","solutions":["Shorten/normalize lane_id so the serialized receipt fits the bound.","If LaneExitReceipt legitimately grew, raise MAX_EXIT_RECEIPT_BYTES in the source.","Audit where lane_id is generated and cap its length at creation."],"exampleFix":"// before\nlet lane_id = format!(\"lane-{}\", huge_user_string);\n// after\nlet lane_id = format!(\"lane-{}\", hash_or_truncated_id);","handlingStrategy":"try-catch","validationCode":"let encoded = serde_json::to_vec(&receipt)?;\nassert!(encoded.len() as u64 <= MAX_EXIT_RECEIPT_BYTES);","typeGuard":null,"tryCatchPattern":"// this indicates an invariant bug: log lane_id length and the bound, then report\nif e.to_string().contains(\"exit receipt exceeds size bound\") { log::error!(\"receipt overflow: lane_id len={}\", lane_id.len()); }","preventionTips":["Bound lane_id length at creation time.","If extending LaneExitReceipt, revisit MAX_EXIT_RECEIPT_BYTES in the same change.","Add a unit test asserting a maximal lane_id serializes under the bound."],"tags":["exit-receipt","size-limit","invariant"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}