xai-org/grok-build · error · io::Error
{}
Error message
{} What it means
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.
Source
Thrown at crates/codegen/xai-grok-tools/src/persistence.rs:319
file.write_all(&json).await?;
file.sync_all().await?;
drop(file);
Self::publish_durable(path, &tmp_path).await
}
.await;
Self::cleanup_temp_on_error(&tmp_path, result).await
}
async fn cleanup_temp_on_error(tmp_path: &Path, result: io::Result<()>) -> io::Result<()> {
if result.is_err() {
let _ = tokio::fs::remove_file(tmp_path).await;
}
result
}
fn prepare_write(path: &Path, value: &serde_json::Value) -> io::Result<(PathBuf, Vec<u8>)> {
let json = serde_json::to_vec_pretty(value)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
Ok((path.with_extension("json.tmp"), json))
}
async fn replace_state_path(path: &Path, tmp_path: &Path) -> io::Result<()> {
if path.is_dir() {
tracing::warn!(
"Resources state path {:?} is a directory — removing before write",
path
);
tokio::fs::remove_dir_all(path).await?;
}
tokio::fs::rename(tmp_path, path).await
}
#[cfg(not(windows))]
async fn publish_durable(path: &Path, tmp_path: &Path) -> io::Result<()> {
// A bare filename has an empty parent, so the write would land in the server's own directory, shared by every session.
let parent = pathView on GitHub (pinned to bc7f02eddd)
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.
Example fix
// before
metrics: { cpu: f64::NAN }, // serialization fails
// after
metrics: { cpu: if cpu.is_finite() { cpu } else { 0.0 } }, Defensive patterns
Strategy: validation
Validate before calling
fn snapshot_is_serializable(s: &ResourcesState) -> bool {
serde_json::to_value(s).is_ok()
} Try / catch
match persistence.save_and_flush(snapshot).await {
Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {
tracing::error!("snapshot serialization failed: {e}");
sanitize_and_resave(snapshot).await?;
}
other => other.map_err(Into::into),
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- failed to build request: {e}
- <serde_json deserialization error for summary.json>
- <serde_json serialization error for Summary>
- {}
- Failed to serialize request: {e}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/e378c3b1d4c3e5b9.
Report an issue: GitHub.