{"record":{"id":"ffca670b6d7e897c","repo":"sinelaw/fresh","slug":"serde-json-serialization-error","errorCode":null,"errorMessage":"(serde_json serialization error)","messagePattern":"\\(serde_json serialization error\\)","errorType":"exception","errorClass":"io::Error (InvalidData)","httpStatus":null,"severity":"warning","filePath":"crates/fresh-editor-core/src/model/buffer/save.rs","lineNumber":387,"sourceCode":"                m.uid.unwrap_or(0),\n                m.gid.unwrap_or(0),\n                m.permissions.as_ref().map(|p| p.mode()).unwrap_or(0o644),\n            )\n        })\n        .unwrap_or((0, 0, 0o644));\n    #[cfg(not(unix))]\n    let (uid, gid, mode) = (0u32, 0u32, 0o644u32);\n\n    let recovery = crate::recovery_types::InplaceWriteRecovery::new(\n        dest_path.to_path_buf(),\n        temp_path.to_path_buf(),\n        uid,\n        gid,\n        mode,\n    );\n\n    let json = serde_json::to_string_pretty(&recovery)\n        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;\n\n    fs.write_file(meta_path, json.as_bytes())\n}\n\n/// Write using in-place mode to preserve file ownership.\n///\n/// This is used when the file is owned by a different user and we need\n/// to write directly to the existing file to preserve its ownership.\n///\n/// The approach:\n/// 1. Write the recipe to a temp file first (reads from original, writes to temp)\n/// 2. Stream the temp file content to the destination file (truncates and writes)\n/// 3. Delete the temp file\n///\n/// This avoids the bug where truncating the destination before reading Copy chunks\n/// would corrupt the file. It also works for huge files since we stream in chunks.\npub(super) fn save_with_inplace_write(\n    fs: &Arc<dyn FileSystem + Send + Sync>,","sourceCodeStart":369,"sourceCodeEnd":405,"githubUrl":"https://github.com/sinelaw/fresh/blob/67894ca5463dbd7a89bb31add4627c27d6b79d83/crates/fresh-editor-core/src/model/buffer/save.rs#L369-L405","documentation":"write_inplace_recovery_meta fails when serde_json cannot serialize the recovery metadata struct (uid/gid/mode plus recovery info) to pretty JSON. Serialization of this struct should be infallible in practice, so this signals an unexpected serde failure. Returned as InvalidData wrapping the serde error.","triggerScenarios":"Calling write_inplace_recovery_meta (from save_with_inplace_write) when serde_json::to_string_pretty(&recovery) fails, e.g. a map with non-string keys or a value type serde_json cannot represent (NaN, non-string map keys).","commonSituations":"Customizing the recovery struct with new fields that aren't JSON-serializable (f32 NaN, HashMap with non-string keys).","solutions":["Audit the recovery struct fields for JSON-incompatible types (non-string map keys, NaN floats).","Derive Serialize/Deserialize correctly and replace unsupported types (e.g. use BTreeMap<String, _>).","If a float can be NaN, serialize it as Option or a string."],"exampleFix":"// before\noffsets: HashMap<u64, usize>,\n// after\noffsets: BTreeMap<String, usize>, // keys must be strings for JSON","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let json = serde_json::to_string_pretty(&recovery)\n    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;\n// caller:\nif let Err(e) = write_inplace_recovery_meta(...).await {\n    log::warn!(\"recovery meta not written: {e}; continuing save\");\n}","preventionTips":["Keep recovery structs strictly JSON-serializable (String keys, no NaN)","Round-trip test serialization of the recovery struct in unit tests","Treat recovery-meta write failure as non-fatal where possible"],"tags":["serialization","json","recovery"],"backgroundTag":"json-marshal-failed","analyzedSha":"67894ca5463dbd7a89bb31add4627c27d6b79d83","analyzedAt":"2026-09-13T15:04:03.701Z","contentChangedAt":"2026-09-13T15:04:03.701Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}