{"record":{"id":"076789dd7f73beeb","repo":"astrid-runtime/astrid","slug":"corpus-input-changed-while-its-baseline-snapshot-w","errorCode":null,"errorMessage":"corpus input changed while its baseline snapshot was captured","messagePattern":"corpus input changed while its baseline snapshot was captured","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-chunker-evidence/src/corpus.rs","lineNumber":527,"sourceCode":"}\n\nfn include_walker_entry(recursive: bool, depth: usize, is_directory: bool, name: &str) -> bool {\n    !recursive || depth != 1 || !is_directory || !EXCLUDED_TOP_LEVEL_DIRECTORIES.contains(&name)\n}\n\nfn snapshot_file(path: &Path) -> Result<FileSnapshot> {\n    let file = File::open(path)\n        .with_context(|| format!(\"open corpus input for snapshot {}\", path.display()))?;\n    let expected_bytes = file\n        .metadata()\n        .with_context(|| format!(\"stat corpus input {}\", path.display()))?\n        .len();\n    let mut reader = HashingReader::new(BufReader::with_capacity(READER_CAPACITY, file));\n    std::io::copy(&mut reader, &mut std::io::sink())\n        .with_context(|| format!(\"snapshot corpus input {}\", path.display()))?;\n    let snapshot = reader.finish();\n    if snapshot.logical_bytes != expected_bytes {\n        bail!(\"corpus input changed while its baseline snapshot was captured\");\n    }\n    Ok(snapshot)\n}\n\nfn validate_label(label: &str) -> Result<()> {\n    if label.is_empty()\n        || !label\n            .bytes()\n            .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'-')\n    {\n        bail!(\"corpus label must contain only lowercase ASCII letters, digits, and hyphens\");\n    }\n    Ok(())\n}\n\nfn validate_relative_git_path(path: &Path) -> Result<()> {\n    if path.as_os_str().is_empty()\n        || path.is_absolute()","sourceCodeStart":509,"sourceCodeEnd":545,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-chunker-evidence/src/corpus.rs#L509-L545","documentation":"`snapshot_file` hashes a file while streaming it and compares the byte count it saw (`snapshot.logical_bytes`) with the size taken before reading (`expected_bytes`). A mismatch means the file was modified concurrently while the baseline snapshot was being captured, so the snapshot is torn/inconsistent and the library aborts.","triggerScenarios":"A file is written, truncated, appended, or rotated between the `metadata().len()` read and the completion of the `std::io::copy` hash inside `snapshot_file` (called by `Input::File`, `collect_files`, `validate_current_file`).","commonSituations":"Corpus files being regenerated by another process during benchmark setup, log files still being appended to, editors/tmp-file swaps (`mv` atomic renames) landing mid-snapshot, or files on actively-changing network shares.","solutions":["Ensure no other process writes to the corpus while the snapshot runs; stop generators/sync daemons first","Copy the corpus to a stable, read-only location and snapshot the copy","Re-run the snapshot; if it keeps failing, find the writer with `lsof <path>` or `fuser`"],"exampleFix":"// before\nlet snap = snapshot_file(&path, path.metadata()?.len())?; // file still being written\n// after\nstd::fs::copy(&path, &stable_copy)?; // freeze input first\nlet snap = snapshot_file(&stable_copy, stable_copy.metadata()?.len())?;","handlingStrategy":"validation","validationCode":"let before = std::fs::metadata(path)?.len();\nsnapshot_file(path, before)?;\nif std::fs::metadata(path)?.len() != before { /* file changed again; retry */ }","typeGuard":"fn stable_size(path: &Path, probes: usize) -> Option<u64> {\n    let mut last = std::fs::metadata(path).ok()?.len();\n    for _ in 0..probes {\n        std::thread::sleep(std::time::Duration::from_millis(50));\n        let now = std::fs::metadata(path).ok()?.len();\n        if now != last { last = now; }\n    }\n    Some(last)\n}","tryCatchPattern":"match snapshot_file(&path, expected_len) {\n    Ok(snap) => snap,\n    Err(e) if e.to_string().contains(\"changed while its baseline snapshot\") => {\n        eprintln!(\"{path:?} is being written; waiting and retrying\");\n        std::thread::sleep(Duration::from_secs(1));\n        retry_snapshot(&path)?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Stop dataset generators, log shippers, and sync clients during snapshotting","Snapshot a frozen copy rather than live files","Quiesce writes (size stable across two probes) before hashing"],"tags":["rust","corpus","concurrency","checksum","race-condition"],"backgroundTag":"checksum-mismatch","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}