{"record":{"id":"a5809e4e71d72b8f","repo":"Hmbown/CodeWhale","slug":"serde-json-serialization-error","errorCode":null,"errorMessage":"<serde_json serialization error>","messagePattern":"<serde_json serialization error>","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/session_manager.rs","lineNumber":1474,"sourceCode":"            Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(false),\n            Err(error) => return Err(error),\n        };\n        let mut marker = Vec::with_capacity(LATE_USAGE_DELETED.len());\n        tombstone\n            .take(u64::try_from(LATE_USAGE_DELETED.len()).unwrap_or(u64::MAX) + 1)\n            .read_to_end(&mut marker)?;\n        if marker != LATE_USAGE_DELETED {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"invalid late usage deletion marker\",\n            ));\n        }\n        Ok(true)\n    }\n\n    fn write_late_usage_ledger(path: &Path, ledger: &LateUsageLedger) -> io::Result<()> {\n        let bytes = serde_json::to_vec(ledger)\n            .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;\n        if u64::try_from(bytes.len()).unwrap_or(u64::MAX) > MAX_LATE_USAGE_LEDGER_BYTES {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"late usage ledger exceeds its size bound\",\n            ));\n        }\n        write_atomic(path, &bytes)\n    }\n\n    fn load_late_usage_unlocked(path: &Path) -> io::Result<LateUsageLedger> {\n        let file = match open_private_read_file(path) {\n            Ok(file) => file,\n            Err(error) if error.kind() == io::ErrorKind::NotFound => {\n                return Ok(LateUsageLedger::default());\n            }\n            Err(error) => return Err(error),\n        };\n        let metadata = file.metadata()?;","sourceCodeStart":1456,"sourceCodeEnd":1492,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/session_manager.rs#L1456-L1492","documentation":"Writing the late-usage ledger serializes the LateUsageLedger struct to JSON with serde_json::to_vec; if serialization fails, the error is wrapped into an io::Error (ErrorKind::InvalidData) whose message is the serde_json error text, e.g. 'invalid type: ...' or 'key must be a string'. In practice serde_json serialization of a plain ledger struct only fails if the data model holds values serde cannot represent, most commonly map keys that are not strings. Thrown from write_late_usage_ledger at crates/tui/src/session_manager.rs:1474.","triggerScenarios":"Calling write_late_usage_ledger (directly or via the session manager's persist path) when the LateUsageLedger contains data serde_json cannot serialize — typically a HashMap/BTreeMap with non-string keys (e.g. u64 keys from session IDs) or a custom Serialize impl that returns an error.","commonSituations":"A code change introduced non-string map keys or new fields with unserializable types into LateUsageLedger; a hand-built ledger in a test/tooling path contains NaN-like or invalid values; custom Serialize implementations that emit errors for invariants (e.g. empty required keys).","solutions":["Read the wrapped serde_json message inside this io::Error — it names the exact field/key that failed.","Ensure all map keys in LateUsageLedger are Strings; convert numeric session IDs to strings before inserting.","Check recent changes to the LateUsageLedger struct for fields with custom Serialize impls that can error; fix the impl or the invariant it enforces.","If the ledger came from an older format migrated in memory, re-derive it from defaults rather than feeding stale structures into write_late_usage_ledger."],"exampleFix":"// before: numeric keys make serde_json fail\nlet mut ledger: LateUsageLedger = Default::default();\nledger.entries.insert(session_id_u64, entry);\n// after: use String keys\nledger.entries.insert(session_id_u64.to_string(), entry);","handlingStrategy":"validation","validationCode":"// ensure ledger data is serializable before calling the writer\nlet probe = serde_json::to_vec(&ledger);\nif let Err(e) = probe {\n    eprintln!(\"ledger not serializable: {e}\"); // fix non-string map keys / custom Serialize\n}","typeGuard":"fn ledger_is_serializable(ledger: &LateUsageLedger) -> bool {\n    serde_json::to_vec(ledger).is_ok()\n}","tryCatchPattern":"match write_late_usage_ledger(path, &ledger) {\n    Ok(()) => {}\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => {\n        eprintln!(\"ledger serialization failed: {e}\"); // message carries serde_json detail\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Use String keys for every map inside LateUsageLedger (serde_json cannot serialize integer map keys).","Avoid custom Serialize impls in ledger types that can return Err for ordinary data.","Add a round-trip unit test (to_vec then from_slice) whenever the ledger struct changes.","Clippy/lint for HashMap<non-string, _> fields in persisted structs."],"tags":["serde","json","serialization","session-state"],"backgroundTag":"json-serialization-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}