astrid-runtime/astrid · error

capsule source changed while archiving

Error message

capsule source changed while archiving {path}

What it means

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.

Solutions

  1. Re-run the archive/publish command once no process is modifying the source directory.
  2. Quiesce build watchers/dev servers or run publish from a clean, immutable build output directory.
  3. Snapshot the source first (copy to a temp dir, or use the same canonical_capsule_archive output) and publish the snapshot.

Example fix

# before: publishing a live watched directory
publish ./dev-capsule  # watcher mutates files mid-archive

# after: snapshot then publish
cp -r ./dev-capsule /tmp/snap && publish /tmp/snap
Defensive patterns

Strategy: retry

Validate before calling

fn source_is_quiet(dir: &Path, quiet_secs: u64) -> bool {
    // no file modified within the window -> safe to archive
    walkdir(dir).all(|e| {
        e.metadata().and_then(|m| m.modified()).map_or(false, |t| {
            t.elapsed().map(|d| d.as_secs() > quiet_secs).unwrap_or(true)
        })
    })
}

Try / catch

for attempt in 0..3 {
    match publish(...) {
        Err(e) if e.to_string().contains("changed while archiving") => { sleep(backoff(attempt)); continue; }
        other => { other?; break; }
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/347c036d28b32ef0. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-capsule-install/src/storage.rs:884

    header.set_uid(0);
    header.set_gid(0);
    header.set_mtime(0);
    header.set_mode(if metadata.is_dir() { 0o755 } else { 0o644 });
    if metadata.is_dir() {
        header.set_entry_type(EntryType::Directory);
        header.set_size(0);
        header.set_cksum();
        builder
            .append_data(&mut header, path, io::empty())
            .with_context(|| format!("append capsule directory {path}"))?;
    } else {
        let mut file =
            File::open(root.join(relative)).with_context(|| format!("open capsule file {path}"))?;
        let mut bytes = Vec::new();
        file.read_to_end(&mut bytes)
            .with_context(|| format!("read capsule file {path}"))?;
        if bytes.len() as u64 != metadata.len() {
            bail!("capsule source changed while archiving {path}");
        }
        header.set_entry_type(EntryType::Regular);
        header.set_size(bytes.len() as u64);
        header.set_cksum();
        builder
            .append_data(&mut header, path, Cursor::new(&bytes))
            .with_context(|| format!("append capsule file {path}"))?;
        // Ensure the source was not swapped while it was read. The archive is
        // only authoritative after a caller has separately verified its
        // digest/receipt; this check turns a common TOCTOU into a hard error.
        let mut second = File::open(root.join(relative))?;
        let mut second_bytes = Vec::new();
        second.read_to_end(&mut second_bytes)?;
        if second_bytes != bytes {
            bail!("capsule source changed while archiving {path}");
        }
    }
    Ok(())

View on GitHub (pinned to affd8760f4)