{"record":{"id":"29720e0747ea8ea7","repo":"xai-org/grok-build","slug":"summary-json-is-empty-0-bytes","errorCode":null,"errorMessage":"summary.json is empty (0 bytes): {}","messagePattern":"summary\\.json is empty \\(0 bytes\\): (.+?)","errorType":"validation","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/session/storage/summary_write.rs","lineNumber":408,"sourceCode":"    let mut summary = read_summary(summary_path)?;\n    let absent_title_applied = summary.apply_patch(patch, Utc::now());\n    write_summary_atomic(summary_path, &summary)?;\n    Ok(absent_title_applied)\n}\n\nfn open_lock_file(path: &Path) -> io::Result<File> {\n    OpenOptions::new()\n        .read(true)\n        .write(true)\n        .create(true)\n        .truncate(false)\n        .open(path)\n}\n\nfn read_summary(path: &Path) -> io::Result<Summary> {\n    let bytes = std::fs::read(path)?;\n    if bytes.is_empty() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"summary.json is empty (0 bytes): {}\", path.display()),\n        ));\n    }\n    serde_json::from_slice::<Summary>(&bytes)\n        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))\n}\n\nfn write_summary_atomic(summary_path: &Path, summary: &Summary) -> io::Result<()> {\n    let bytes = serde_json::to_vec_pretty(summary)\n        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;\n    crate::session::storage::write_bytes_atomic(summary_path, &bytes)\n}\n\n#[cfg(test)]\nthread_local! {\n    static RESTORE_MTIME_FAULT: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };\n}","sourceCodeStart":390,"sourceCodeEnd":426,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/session/storage/summary_write.rs#L390-L426","documentation":"read_summary reads a session's summary.json and rejects a zero-length file before attempting JSON parsing. This library writes summary.json atomically, so a 0-byte file means the file was truncated or created by a non-atomic writer (or a crash/interrupted write left a stub). The io::ErrorKind::InvalidData error carries the file path to help locate the corrupt file.","triggerScenarios":"Calling read_summary (directly or via most_recent_local_summary_for_cwd_in_view, repair_worktree_identity, read_modify_write, or the auto-title paths) on a summary.json that exists but is 0 bytes — e.g. after a crash during a legacy non-atomic write, an external tool touching the file, or a filesystem sync that truncated it.","commonSituations":"Disk-full during an old non-atomic write; a user or backup tool creating an empty summary.json; crash between file creation and content flush; cloning/syncing session dirs with a sync tool that creates placeholder empty files.","solutions":["Delete the empty summary.json (or restore it from backup) so the session can regenerate it on next write","Re-run the operation; readers that fail on one session's empty summary are usually scanning multiple sessions — ensure the code skips unreadable sessions instead of aborting","Verify writes go through write_summary_atomic/write_bytes_atomic, never plain fs::write to the final path","Check disk space and filesystem health if truncation happened repeatedly"],"exampleFix":"// before\nlet summary = read_summary(&path)?;\n// after\nlet summary = match read_summary(&path) {\n    Ok(s) => s,\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => { /* skip/log corrupt summary */ continue; }\n    Err(e) => return Err(e),\n};","handlingStrategy":"try-catch","validationCode":"let meta = std::fs::metadata(&path)?;\nif meta.len() == 0 { /* skip or delete corrupt summary */ }","typeGuard":"fn is_readable_summary(path: &Path) -> bool {\n    std::fs::read(path).map(|b| !b.is_empty()).unwrap_or(false)\n}","tryCatchPattern":"match read_summary(&path) {\n    Ok(s) => s,\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => {\n        let _ = std::fs::remove_file(&path); // drop corrupt file, regenerate later\n        Summary::default()\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always write summary.json via the atomic write helpers, never plain fs::write","When scanning multiple sessions, skip InvalidData errors instead of aborting the scan","Monitor disk space; truncation is often disk-full during write","Keep backups or regenerate summaries instead of failing hard on one corrupt file"],"tags":["io","serialization","corrupt-file","atomic-write"],"backgroundTag":"empty-or-corrupt-state-file","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}