{"record":{"id":"e378c3b1d4c3e5b9","repo":"xai-org/grok-build","slug":"error-e378c3","errorCode":null,"errorMessage":"{}","messagePattern":"\\{\\}","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-tools/src/persistence.rs","lineNumber":319,"sourceCode":"            file.write_all(&json).await?;\n            file.sync_all().await?;\n            drop(file);\n            Self::publish_durable(path, &tmp_path).await\n        }\n        .await;\n        Self::cleanup_temp_on_error(&tmp_path, result).await\n    }\n\n    async fn cleanup_temp_on_error(tmp_path: &Path, result: io::Result<()>) -> io::Result<()> {\n        if result.is_err() {\n            let _ = tokio::fs::remove_file(tmp_path).await;\n        }\n        result\n    }\n\n    fn prepare_write(path: &Path, value: &serde_json::Value) -> io::Result<(PathBuf, Vec<u8>)> {\n        let json = serde_json::to_vec_pretty(value)\n            .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;\n        Ok((path.with_extension(\"json.tmp\"), json))\n    }\n\n    async fn replace_state_path(path: &Path, tmp_path: &Path) -> io::Result<()> {\n        if path.is_dir() {\n            tracing::warn!(\n                \"Resources state path {:?} is a directory — removing before write\",\n                path\n            );\n            tokio::fs::remove_dir_all(path).await?;\n        }\n        tokio::fs::rename(tmp_path, path).await\n    }\n\n    #[cfg(not(windows))]\n    async fn publish_durable(path: &Path, tmp_path: &Path) -> io::Result<()> {\n        // A bare filename has an empty parent, so the write would land in the server's own directory, shared by every session.\n        let parent = path","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-tools/src/persistence.rs#L301-L337","documentation":"prepare_write serializes the resources state to pretty JSON before an atomic write; on serialization failure it wraps the serde_json error in io::ErrorKind::InvalidData with the message being the serde error text (\"{}\"). This means the in-memory snapshot could not be converted to JSON — a data-model problem, not a disk problem.","triggerScenarios":"Passing a value containing unserializable content to the persistence layer — e.g. NaN/f64 non-finite floats in resource metrics, a map with non-string keys, or a type whose Serialize impl errors (skip_serializing_if misuse is not an error, but custom Serialize returning Err is).","commonSituations":"NaN or Infinity sneaking into measured CPU/memory stats before persisting; version drift where an enum variant's serializer was changed to error on unknown shapes; poisoned data loaded from an older schema then re-saved.","solutions":["Read the wrapped serde message to identify the offending field/type in the snapshot.","Sanitize non-finite floats (NaN/Infinity) to null or 0 before persisting.","Add #[serde(skip_serializing_if = \"Option::is_none\")] / fix the custom Serialize impl that errors.","Add a round-trip unit test (serialize -> deserialize) over the snapshot type to catch regressions."],"exampleFix":"// before\nmetrics: { cpu: f64::NAN },  // serialization fails\n// after\nmetrics: { cpu: if cpu.is_finite() { cpu } else { 0.0 } },","handlingStrategy":"validation","validationCode":"fn snapshot_is_serializable(s: &ResourcesState) -> bool {\n    serde_json::to_value(s).is_ok()\n}","typeGuard":null,"tryCatchPattern":"match persistence.save_and_flush(snapshot).await {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {\n        tracing::error!(\"snapshot serialization failed: {e}\");\n        sanitize_and_resave(snapshot).await?;\n    }\n    other => other.map_err(Into::into),\n}","preventionTips":["Keep NaN/Infinity out of state (replace with None or clamped values).","Add round-trip serialize/deserialize tests for all persisted types.","Run serde validation in debug builds before persisting.","Review custom Serialize impls for error paths."],"tags":["serialization","serde","invalid-data","persistence"],"backgroundTag":"serialization-failed","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}