astrid-runtime/astrid · error
failed to replace signed capsule archive: {error}
Error message
failed to replace signed capsule archive: {error} What it means
rewrite_with_provenance rebuilds a signed capsule archive into a staged temporary file and then atomically persists it over the original archive path with persist(). If the OS refuses to replace the target (persist fails), the library wraps the persist error with this message so the caller knows the signed archive was built but could not be swapped into place.
Source
Thrown at crates/astrid-build/src/artifact.rs:430
0o755
} else {
0o644
};
let mut header = deterministic_header(entry.size(), mode, entry_type);
target
.append_data(&mut header, &path, &mut entry)
.with_context(|| format!("failed to copy capsule entry '{path}'"))?;
}
let mut header =
deterministic_header(envelope.len() as u64, 0o644, tar::EntryType::Regular);
target.append_data(&mut header, PROVENANCE_FILE, envelope)?;
let encoder = target.into_inner()?;
encoder.finish()?;
}
staged.as_file_mut().sync_all()?;
staged
.persist(archive_path)
.map_err(|error| anyhow::anyhow!("failed to replace signed capsule archive: {error}"))?;
Ok(())
}
/// Construct a metadata-stable GNU tar header for synthesized or rewritten
/// entries. Content and executable intent remain significant; host ownership
/// and wall-clock timestamps do not.
fn deterministic_header(size: u64, mode: u32, entry_type: tar::EntryType) -> tar::Header {
let mut header = tar::Header::new_gnu();
header.set_size(size);
header.set_mode(mode);
header.set_uid(0);
header.set_gid(0);
header.set_mtime(tar::DETERMINISTIC_TIMESTAMP);
header.set_entry_type(entry_type);
header.set_cksum();
header
}
View on GitHub (pinned to affd8760f4)
Solutions
- Check the wrapped inner error: fix permissions on archive_path and its parent directory (writable by the build user).
- Close other processes holding the archive open (editors, AV scanners, prior build steps) and retry.
- Ensure the staged temp file and archive_path are on the same filesystem so the atomic rename can succeed.
- Remove read-only/immutable flags from the existing archive before signing.
Example fix
// before: destination read-only, persist fails // -rw-r--r-- root capsule.tar.zst (owned by root, build runs as ci) // after: ensure ownership/permissions before signing chown ci:ci capsule.tar.zst && chmod u+w capsule.tar.zst cargo build --release
Defensive patterns
Strategy: fallback
Validate before calling
// Pre-flight: destination must be writable and not read-only before signing
let meta = std::fs::metadata(archive_path)?;
if meta.permissions().readonly() {
let mut perms = meta.permissions();
perms.set_permissions(perms.mode() & !0o444 | 0o200);
std::fs::set_permissions(archive_path, perms)?;
} Try / catch
match sign_archive(&archive_path, &key).await {
Ok(()) => {},
Err(e) if e.to_string().contains("failed to replace signed capsule archive") => {
// fallback: write to a sibling path so the build still produces the artifact
let alt = archive_path.with_extension("tar.zst.new");
rewrite_with_provenance(&alt, provenance).await?;
}
Err(e) => return Err(e),
} Prevention
- Keep the staged temp file and the destination on the same filesystem.
- Ensure build users have write permission on the artifact directory.
- Exclude artifact paths from AV/indexers that hold files open during builds.
- Clear read-only flags inherited from artifact sync tools before signing.
When it happens
Trigger: Calling sign_archive (which calls rewrite_with_provenance) when tempfile::persist fails to rename the staged file onto archive_path — target path locked/open by another process, permission denied on the destination or its directory, cross-filesystem rename, or destination removed/immutable.
Common situations: Antivirus, backup, or another build process holding the archive open (common on Windows); read-only output directory or CI cache permissions; archive_path on a different mount than the staging directory; the file marked read-only from a previous artifact sync.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- failed to persist {}: {e}
- audit fixture
- non-empty audit
- leftover capsule authority receipt is not a regular file: {}
- quarantined capsule authority bytes changed: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/e7bff2fba701fb24.
Report an issue: GitHub.