sinelaw/fresh · warning · io::Error (InvalidData)
(serde_json serialization error)
Error message
(serde_json serialization error)
What it means
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.
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.
Example fix
// before offsets: HashMap<u64, usize>, // after offsets: BTreeMap<String, usize>, // keys must be strings for JSON
Defensive patterns
Strategy: try-catch
Try / catch
let json = serde_json::to_string_pretty(&recovery)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
// caller:
if let Err(e) = write_inplace_recovery_meta(...).await {
log::warn!("recovery meta not written: {e}; continuing save");
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: Customizing the recovery struct with new fields that aren't JSON-serializable (f32 NaN, HashMap with non-string keys).
Understand the failure class
Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.
Related errors
- Metadata file exists but couldn't be read
- Invalid calibration file
- Chunk content not found
- Chunk file not found
- Chunked recovery data not found
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/ffca670b6d7e897c.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-editor-core/src/model/buffer/save.rs:387
m.uid.unwrap_or(0),
m.gid.unwrap_or(0),
m.permissions.as_ref().map(|p| p.mode()).unwrap_or(0o644),
)
})
.unwrap_or((0, 0, 0o644));
#[cfg(not(unix))]
let (uid, gid, mode) = (0u32, 0u32, 0o644u32);
let recovery = crate::recovery_types::InplaceWriteRecovery::new(
dest_path.to_path_buf(),
temp_path.to_path_buf(),
uid,
gid,
mode,
);
let json = serde_json::to_string_pretty(&recovery)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
fs.write_file(meta_path, json.as_bytes())
}
/// Write using in-place mode to preserve file ownership.
///
/// This is used when the file is owned by a different user and we need
/// to write directly to the existing file to preserve its ownership.
///
/// The approach:
/// 1. Write the recipe to a temp file first (reads from original, writes to temp)
/// 2. Stream the temp file content to the destination file (truncates and writes)
/// 3. Delete the temp file
///
/// This avoids the bug where truncating the destination before reading Copy chunks
/// would corrupt the file. It also works for huge files since we stream in chunks.
pub(super) fn save_with_inplace_write(
fs: &Arc<dyn FileSystem + Send + Sync>,View on GitHub (pinned to 67894ca546)