{"record":{"id":"f0a6b7c5ba978e43","repo":"Hmbown/CodeWhale","slug":"dynamic-wrapped-serde-json-parse-error-truncate","errorCode":null,"errorMessage":"<dynamic: wrapped serde_json parse error>","messagePattern":"<dynamic: wrapped serde_json parse error>","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/truncate.rs","lineNumber":172,"sourceCode":"    crate::utils::write_atomic(&sidecar, &encoded)?;\n    Ok(sidecar)\n}\n\npub(crate) fn read_legacy_spillover_ownership(\n    payload_path: &Path,\n) -> io::Result<LegacySpilloverOwnership> {\n    let sidecar = legacy_spillover_ownership_path(payload_path);\n    if std::fs::symlink_metadata(&sidecar)?\n        .file_type()\n        .is_symlink()\n    {\n        return Err(io::Error::new(\n            io::ErrorKind::PermissionDenied,\n            \"legacy spillover ownership sidecar must not be a symlink\",\n        ));\n    }\n    let ownership = serde_json::from_slice::<LegacySpilloverOwnership>(&std::fs::read(sidecar)?)\n        .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;\n    if ownership.schema_version != LEGACY_SPILLOVER_OWNER_SCHEMA_VERSION {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            \"unsupported legacy spillover ownership schema\",\n        ));\n    }\n    Ok(ownership)\n}\n\n/// Resolve the spillover-file path for a SHA256 content hash. Separate\n/// namespace (`sha_<hex>.txt`) from the tool-call-id files so legacy\n/// SHA-addressed evidence can be recognized without colliding with\n/// tool-call references. Retrieval still requires matching ownership\n/// metadata. `sha` must be the raw 64-char lowercase hex digest —\n/// case-insensitive matching is done by the caller.\n#[must_use]\npub fn sha_spillover_path(sha: &str) -> Option<PathBuf> {\n    let sha = sha.trim().to_ascii_lowercase();","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/truncate.rs#L154-L190","documentation":"read_legacy_spillover_ownership reads a JSON sidecar describing legacy spillover ownership and deserializes it with serde_json. When the file's bytes are not valid JSON for the LegacySpilloverOwnership struct, the serde error is wrapped into an io::Error with ErrorKind::InvalidData. This surfaces as 'dynamic: wrapped serde_json parse error'.","triggerScenarios":"Calling read_legacy_spillover_ownership on a sidecar file whose contents are malformed JSON, missing required fields of LegacySpilloverOwnership (e.g. schema_version), or contain wrong-typed values.","commonSituations":"A partially-written or truncated sidecar from a crash; a sidecar hand-edited to invalid JSON; a schema drift where an older version wrote different fields; a non-JSON file at the sidecar path.","solutions":["Delete or fix the corrupt sidecar file so it parses as LegacySpilloverOwnership JSON","Verify the sidecar contains a schema_version field matching LEGACY_SPILLOVER_OWNER_SCHEMA_VERSION","Regenerate the sidecar by re-running the spillover migration/creation path","Inspect the inner serde error message to identify the exact JSON field/type problem"],"exampleFix":"// before: hard failure on corrupt sidecar\nlet ownership = read_legacy_spillover_ownership(&sidecar)?;\n// after: tolerate corrupt/absent legacy sidecar\nlet ownership = match read_legacy_spillover_ownership(&sidecar) {\n    Ok(o) => Some(o),\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => None,\n    Err(e) => return Err(e),\n};","handlingStrategy":"try-catch","validationCode":"// pre-check: is it plausible JSON with a schema_version?\nlet raw = std::fs::read_to_string(&sidecar)?;\nif !raw.trim_start().starts_with('{') || !raw.contains(\"schema_version\") {\n    // treat as corrupt/legacy; recreate instead of calling the reader\n}","typeGuard":"fn is_valid_sidecar(raw: &str) -> bool {\n    serde_json::from_str::<serde_json::Value>(raw)\n        .map(|v| v.get(\"schema_version\").is_some())\n        .unwrap_or(false)\n}","tryCatchPattern":"match read_legacy_spillover_ownership(&sidecar) {\n    Ok(o) => o,\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => recreate_sidecar(&sidecar)?,\n    Err(e) => return Err(e),\n}","preventionTips":["Write the sidecar atomically (temp file + rename) so it is never seen truncated","Validate sidecar JSON after every write","Version-gate schema changes and ship migration code","Never hand-edit sidecar files in place"],"tags":["io","serde","json-parse-error","corrupt-file"],"backgroundTag":"json-parse-error","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"}