{"record":{"id":"347c036d28b32ef0","repo":"astrid-runtime/astrid","slug":"capsule-source-changed-while-archiving-path","errorCode":null,"errorMessage":"capsule source changed while archiving {path}","messagePattern":"capsule source changed while archiving (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/storage.rs","lineNumber":884,"sourceCode":"    header.set_uid(0);\n    header.set_gid(0);\n    header.set_mtime(0);\n    header.set_mode(if metadata.is_dir() { 0o755 } else { 0o644 });\n    if metadata.is_dir() {\n        header.set_entry_type(EntryType::Directory);\n        header.set_size(0);\n        header.set_cksum();\n        builder\n            .append_data(&mut header, path, io::empty())\n            .with_context(|| format!(\"append capsule directory {path}\"))?;\n    } else {\n        let mut file =\n            File::open(root.join(relative)).with_context(|| format!(\"open capsule file {path}\"))?;\n        let mut bytes = Vec::new();\n        file.read_to_end(&mut bytes)\n            .with_context(|| format!(\"read capsule file {path}\"))?;\n        if bytes.len() as u64 != metadata.len() {\n            bail!(\"capsule source changed while archiving {path}\");\n        }\n        header.set_entry_type(EntryType::Regular);\n        header.set_size(bytes.len() as u64);\n        header.set_cksum();\n        builder\n            .append_data(&mut header, path, Cursor::new(&bytes))\n            .with_context(|| format!(\"append capsule file {path}\"))?;\n        // Ensure the source was not swapped while it was read. The archive is\n        // only authoritative after a caller has separately verified its\n        // digest/receipt; this check turns a common TOCTOU into a hard error.\n        let mut second = File::open(root.join(relative))?;\n        let mut second_bytes = Vec::new();\n        second.read_to_end(&mut second_bytes)?;\n        if second_bytes != bytes {\n            bail!(\"capsule source changed while archiving {path}\");\n        }\n    }\n    Ok(())","sourceCodeStart":866,"sourceCodeEnd":902,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage.rs#L866-L902","documentation":"Raised by append_entry after reading a file into memory for the canonical archive: the byte count read differs from the file length recorded during the earlier directory walk (collect_entries). This detects the source mutating between enumeration and archiving, which would make the tar header size and actual content inconsistent.","triggerScenarios":"canonical_capsule_archive running while a file in the source directory is written, truncated, appended, or deleted/replaced concurrently; metadata.len() from the walk no longer matches the bytes read at append time.","commonSituations":"A dev server or build watcher regenerating files inside the capsule during publish; an editor with autosave writing while archiving; CI racing between build and publish steps.","solutions":["Re-run the archive/publish command once no process is modifying the source directory.","Quiesce build watchers/dev servers or run publish from a clean, immutable build output directory.","Snapshot the source first (copy to a temp dir, or use the same canonical_capsule_archive output) and publish the snapshot."],"exampleFix":"# before: publishing a live watched directory\npublish ./dev-capsule  # watcher mutates files mid-archive\n\n# after: snapshot then publish\ncp -r ./dev-capsule /tmp/snap && publish /tmp/snap","handlingStrategy":"retry","validationCode":"fn source_is_quiet(dir: &Path, quiet_secs: u64) -> bool {\n    // no file modified within the window -> safe to archive\n    walkdir(dir).all(|e| {\n        e.metadata().and_then(|m| m.modified()).map_or(false, |t| {\n            t.elapsed().map(|d| d.as_secs() > quiet_secs).unwrap_or(true)\n        })\n    })\n}","typeGuard":null,"tryCatchPattern":"for attempt in 0..3 {\n    match publish(...) {\n        Err(e) if e.to_string().contains(\"changed while archiving\") => { sleep(backoff(attempt)); continue; }\n        other => { other?; break; }\n    }\n}","preventionTips":["Stop file watchers/dev servers before publishing","Publish from an immutable snapshot or clean CI artifact","Re-run publish after transient races instead of ignoring the error"],"tags":["concurrency","archive","capsule"],"backgroundTag":"checksum-mismatch","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}