xai-org/grok-build · error · std::io::Error

<serde_json serialization error for Summary>

Error message

<serde_json serialization error for Summary>

What it means

write_summary_atomic serializes the Summary struct to pretty JSON before atomically writing it; a serde_json serialization failure (e.g. a non-string map key or unserializable value inside Summary) is wrapped as io::ErrorKind::InvalidData. With a plain derive-based struct this is rare and usually indicates a Map with non-string keys or a custom Serialize impl that fails.

Source

Thrown at crates/codegen/xai-grok-shell/src/session/storage/summary_write.rs:419

        .truncate(false)
        .open(path)
}

fn read_summary(path: &Path) -> io::Result<Summary> {
    let bytes = std::fs::read(path)?;
    if bytes.is_empty() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("summary.json is empty (0 bytes): {}", path.display()),
        ));
    }
    serde_json::from_slice::<Summary>(&bytes)
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}

fn write_summary_atomic(summary_path: &Path, summary: &Summary) -> io::Result<()> {
    let bytes = serde_json::to_vec_pretty(summary)
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
    crate::session::storage::write_bytes_atomic(summary_path, &bytes)
}

#[cfg(test)]
thread_local! {
    static RESTORE_MTIME_FAULT: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
}

#[cfg(test)]
pub(crate) fn fail_next_restore_summary_mtime() {
    RESTORE_MTIME_FAULT.set(true);
}

fn restore_summary_mtime(path: &Path, mtime: std::time::SystemTime) -> io::Result<()> {
    #[cfg(test)]
    if RESTORE_MTIME_FAULT.replace(false) {
        return Err(io::Error::other("injected mtime restore failure"));
    }

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Inspect the inner serde error message to find the unserializable field
  2. Change non-string map keys to String (or use #[serde(with)] helpers)
  3. Ensure all Summary fields' types implement Serialize compatibly with JSON
  4. Add a unit test that serializes a fully-populated Summary to catch regressions

Example fix

// before
struct Summary { meta: HashMap<Uuid, String> }
// after
struct Summary { meta: HashMap<String, String> } // or BTreeMap<String, _> with uuid::Uuid keys via serde
let meta: HashMap<String, String> = original.into_iter().map(|(k, v)| (k.to_string(), v)).collect();
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-validate: ensure all map keys in data feeding Summary are strings
assert!(summary.meta.keys().all(|k| k.parse::<uuid::Uuid>().is_ok() || k.is_empty()) || true); // prefer converting keys upstream
type Check = fn(&Summary) -> Result<(), serde_json::Error>;
let validate: Check = |s| serde_json::to_value(s).map(|_| ());

Type guard

fn serializable(s: &Summary) -> bool {
    serde_json::to_vec_pretty(s).is_ok()
}

Try / catch

match write_summary_atomic(&path, &summary) {
    Ok(()) => (),
    Err(e) if e.kind() == io::ErrorKind::InvalidData => {
        eprintln!("Summary not JSON-serializable: {e}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling write_summary_atomic (via repair_worktree_identity or read_modify_write) with a Summary containing data serde_json cannot represent — e.g. a HashMap with non-string keys, an untagged enum hitting an unserializable variant, or a custom Serialize impl returning an error.

Common situations: Introducing a BTreeMap<HashMap<i32,_>,..> or serde_json::Value::Number-typed key into Summary; nesting types with non-string map keys; building Summary from arbitrary parsed JSON with wrong key types.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/7430ed5e353d92f0. Report an issue: GitHub.