{"record":{"id":"5fdeda25c83c2b84","repo":"Hmbown/CodeWhale","slug":"serde-json-deserialization-error-wrapped-as-io-errorkind-5fdeda","errorCode":null,"errorMessage":"(serde_json deserialization error wrapped as io::ErrorKind::InvalidData)","messagePattern":"\\(serde_json deserialization error wrapped as io::ErrorKind::InvalidData\\)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/large_output_router.rs","lineNumber":314,"sourceCode":"pub fn publish_evidence_metadata(\n    session_id: &str,\n    artifact: &EvidenceArtifact,\n) -> io::Result<PathBuf> {\n    let bytes = serde_json::to_vec_pretty(artifact)\n        .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;\n    crate::artifacts::write_session_relative_immutable(\n        session_id,\n        &evidence_metadata_relative_path(&artifact.handle),\n        &bytes,\n    )\n}\n\npub fn read_evidence_metadata(session_id: &str, handle: &str) -> io::Result<EvidenceArtifact> {\n    let relative = evidence_metadata_relative_path(handle);\n    let path = crate::artifacts::session_artifact_absolute_path(session_id, &relative)\n        .ok_or_else(|| io::Error::new(io::ErrorKind::PermissionDenied, \"invalid evidence owner\"))?;\n    let raw = std::fs::read(path)?;\n    serde_json::from_slice(&raw).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))\n}\n\n#[must_use]\npub fn unix_millis_now() -> u64 {\n    std::time::SystemTime::now()\n        .duration_since(std::time::UNIX_EPOCH)\n        .unwrap_or_default()\n        .as_millis()\n        .try_into()\n        .unwrap_or(u64::MAX)\n}\n\n#[must_use]\npub fn evidence_is_expired(artifact: &EvidenceArtifact, now_ms: u64) -> bool {\n    artifact.retention_state == EvidenceRetentionState::Expired\n        || now_ms > artifact.retain_until_unix_ms\n}\n","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/tools/large_output_router.rs#L296-L332","documentation":"read_evidence_metadata reads a JSON sidecar (the evidence artifact metadata file) for a given session/handle pair and deserializes it into EvidenceArtifact. When serde_json fails to parse or validate the file contents, the error is wrapped as an io::Error with ErrorKind::InvalidData. This means the file exists and was readable, but its bytes are not valid JSON for the EvidenceArtifact schema.","triggerScenarios":"Calling read_evidence_metadata(session_id, handle) where the metadata file at the session-scoped evidence path exists but contains corrupt, truncated, hand-edited, or schema-mismatched JSON (e.g. missing required fields of EvidenceArtifact).","commonSituations":"A previous write was interrupted mid-file; a user or script edited the sidecar by hand; the schema_version of EvidenceArtifact changed between app versions so old files no longer deserialize; disk corruption or a partially synced file.","solutions":["Inspect the metadata file at the resolved session artifact path and validate it as JSON (jq or serde_json::from_slice in a test) to see the exact serde message wrapped in the io error.","Delete or restore the corrupt sidecar so the owning tool regenerates it on next evidence write.","If schema drift is the cause, migrate or discard artifacts written by the older version rather than editing files in place.","Wrap the call to treat InvalidData as 'artifact unreadable' and fall back to regenerating the evidence rather than failing the whole operation."],"exampleFix":"// before\nlet artifact = read_evidence_metadata(&session_id, &handle)?;\n// after\nlet artifact = match read_evidence_metadata(&session_id, &handle) {\n    Ok(a) => a,\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {\n        // corrupt/legacy sidecar: regenerate or skip\n        regenerate_or_skip(&session_id, &handle)?\n    }\n    Err(e) => return Err(e),\n};","handlingStrategy":"try-catch","validationCode":"fn evidence_metadata_readable(path: &std::path::Path) -> bool {\n    std::fs::read(path).map(|raw| serde_json::from_slice::<serde_json::Value>(&raw).is_ok()).unwrap_or(false)\n}","typeGuard":"fn is_valid_evidence_json(raw: &[u8]) -> bool {\n    serde_json::from_slice::<crate::tools::EvidenceArtifact>(raw).is_ok()\n}","tryCatchPattern":"match read_evidence_metadata(session_id, handle) {\n    Ok(a) => a,\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => fallback_regenerate(),\n    Err(e) => return Err(e),\n}","preventionTips":["Only write evidence metadata via the owning tool's atomic write path, never by hand","Check schema_version fields before parsing when versions may differ","Treat InvalidData on artifact reads as a signal to regenerate, not to retry"],"tags":["serde","json","io","deserialization","artifacts"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}