{"record":{"id":"ac90588a60c8a275","repo":"Hmbown/CodeWhale","slug":"serde-json-deserialization-error","errorCode":null,"errorMessage":"<serde_json deserialization error>","messagePattern":"<serde_json deserialization error>","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/session_manager.rs","lineNumber":1518,"sourceCode":"            ));\n        }\n        use std::io::Read as _;\n        let mut raw = Vec::with_capacity(\n            usize::try_from(metadata.len().min(MAX_LATE_USAGE_LEDGER_BYTES)).unwrap_or(0),\n        );\n        file.take(MAX_LATE_USAGE_LEDGER_BYTES.saturating_add(1))\n            .read_to_end(&mut raw)?;\n        if u64::try_from(raw.len()).unwrap_or(u64::MAX) > MAX_LATE_USAGE_LEDGER_BYTES {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"late usage ledger {} exceeds its size bound\",\n                    path.display()\n                ),\n            ));\n        }\n        let ledger: LateUsageLedger = serde_json::from_slice(&raw)\n            .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;\n        if ledger.schema_version != CURRENT_LATE_USAGE_SCHEMA_VERSION\n            || ledger.records.len() > MAX_LATE_USAGE_RECORDS_PER_SESSION\n            || ledger.records.iter().any(|record| {\n                !is_sha256_fingerprint(&record.source_fingerprint)\n                    || !is_sha256_fingerprint(&record.turn_fingerprint)\n            })\n        {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"late usage ledger has an unsupported or unbounded shape\",\n            ));\n        }\n        Ok(ledger)\n    }\n\n    fn with_session_write_admission<T>(\n        &self,\n        session_id: &str,","sourceCodeStart":1500,"sourceCodeEnd":1536,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/session_manager.rs#L1500-L1536","documentation":"Loading a session's late-usage ledger from disk failed because the file's bytes are not valid JSON matching the LateUsageLedger schema; serde_json::from_slice returned a parse/deser error which is wrapped as an io::Error of kind InvalidData. The library refuses to tolerate a corrupt or hand-edited ledger file rather than silently starting an empty ledger. The wrapped serde message names the exact offending JSON path or type mismatch.","triggerScenarios":"Calling load_late_usage / load_late_usage_unlocked when the on-disk ledger file contains malformed JSON, a wrong schema (missing schema_version or records fields), or fields whose types do not match the struct (e.g. records as an object, schema_version as a string).","commonSituations":"A crash or full disk truncated the ledger mid-write; a user or script hand-edited or deleted part of the JSON; a ledger written by a different (older or newer) version of the schema whose field shapes differ.","solutions":["Open the ledger file shown in the path and validate it (jq . <ledger.json>) to find the malformed section; repair or regenerate it.","Delete or archive the corrupt ledger file so the manager recreates an empty one for that session.","If you produced the file externally, write it with serde_json::to_string for the same LateUsageLedger struct and the current CURRENT_LATE_USAGE_SCHEMA_VERSION.","Check the file is not truncated at the byte-size bound (MAX_LATE_USAGE_LEDGER_BYTES) — a partially written file fails JSON parsing."],"exampleFix":"// before (corrupt hand-edited ledger)\n{ \"schema_version\": \"1\", \"records\": [] }\n// after (matches LateUsageLedger)\n{ \"schema_version\": 1, \"records\": [] }","handlingStrategy":"validation","validationCode":"let raw = std::fs::read(&ledger_path)?;\nif raw.is_empty() || std::str::from_utf8(&raw).map(|s| serde_json::from_str::<serde_json::Value>(s)).map_err(|_| ()).is_err() {\n    // corrupt or empty: repair, regenerate, or delete before loading\n}\nlet _ok: LateUsageLedger = serde_json::from_slice(&raw)?; // pre-flight parse","typeGuard":"fn is_valid_ledger(bytes: &[u8]) -> bool {\n    serde_json::from_slice::<LateUsageLedger>(bytes).is_ok()\n}","tryCatchPattern":"match manager.load_late_usage(&session_id) {\n    Ok(l) => l,\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {\n        // quarantine corrupt ledger, fall back to a fresh ledger\n        LateUsageLedger::default()\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Never hand-edit ledger JSON; use the library's write path (atomic writes) only.","Validate JSON with jq or a serde pre-parse after any external tool touches the file.","Monitor for truncated files after crashes; write ledgers atomically (write_atomic)."],"tags":["serde","json","io","deserialization","session-state"],"backgroundTag":"json-unmarshal-failed","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"}