astrid-runtime/astrid · critical
InvalidData
InvalidData
Error message
staged executable does not match its locked source handle
What it means
This error is raised during an authenticated staging copy of an executable (prepare_executable_transaction and related staging APIs). After copying the source file into a private staged file, the library re-hashes the staged bytes and compares them against the hash of the locked source handle. A mismatch means the bytes that landed on disk differ from what the locked source contained — the library refuses to proceed rather than install a corrupted or tampered binary, and removes the staged copy.
Source
Thrown at crates/astrid-core/src/platform_fs/windows/io.rs:241
file_name: &str,
) -> io::Result<(PathBuf, String)> {
let source_path = source;
let mut source_file =
open_guarded_regular_file(source_guard, source_path, source_file_contract)?;
let source_identity = file_identity(&source_file)?;
let source_hash = hash_open_file(&mut source_file)?;
source_file.seek(io::SeekFrom::Start(0))?;
let destination = install_dir.join(file_name);
let mut output = create_guarded_private_file(destination_guard, &destination)?;
let mut cleanup = PreparationCleanup::new(destination_guard);
cleanup.track(destination.clone());
let result = (|| {
io::copy(&mut source_file, &mut output)?;
output.flush()?;
output.sync_all()?;
let staged_hash = hash_open_file(&mut output)?;
if staged_hash != source_hash {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"staged executable does not match its locked source handle",
));
}
validate_private_acl_handle(
output.as_raw_handle().cast(),
false,
&destination.display().to_string(),
)?;
Ok(())
})();
drop(output);
if let Err(error) = result {
let _ = remove_guarded_file(destination_guard, &destination);
return Err(error);
}
if let Err(error) = validate_file_contract(
source_file.as_raw_handle().cast(),View on GitHub (pinned to affd8760f4)
Solutions
- Close any process that could be writing to the source executable (other updaters, editors, sync clients) and retry the transaction.
- Add an antivirus/EDR exclusion for the install and staging directories so filters stop rewriting the binary mid-copy.
- Verify free disk space and filesystem health (chkdsk) on the volume holding the staging directory.
- Re-fetch the source executable from a trusted origin so the staged hash matches a known-good source, then retry.
Example fix
// before: copying from a source that another process may rewrite in place let hash = hash_open_file(&mut source_file)?; source_file.seek(SeekFrom::Start(0))?; io::copy(&mut source_file, &mut output)?; // source mutated concurrently -> hash mismatch // after: ensure exclusive access first (open with no share modes, as the library does // via open_guarded_child_locked) and verify identity after copy let identity = file_identity(&source_file)?; let hash = hash_open_file(&mut source_file)?; source_file.seek(SeekFrom::Start(0))?; io::copy(&mut source_file, &mut output)?; assert_eq!(file_identity(&source_file)?, identity, "source changed during copy");
Defensive patterns
Strategy: try-catch
Validate before calling
// Rust: pre-check free space and ensure no competing writers before staging
let meta = fs::metadata(&source)?;
let avail = fs2::available_space(&install_dir)?;
if avail < meta.len() * 2 {
return Err(io::Error::new(io::ErrorKind::StorageFull, "insufficient space for staging"));
} Try / catch
// Rust
match prepare_executable_transaction(/* args */) {
Ok(plan) => commit(plan),
Err(e) if e.kind() == io::ErrorKind::InvalidData
&& e.to_string().contains("staged executable does not match") => {
// integrity failure: kill competing writers, exclude AV paths, retry once
eprintln!("staged copy corrupted (AV or concurrent write?): {e}");
retry_with_exclusive_source_lock();
}
Err(e) => return Err(e),
} Prevention
- Run only one updater/installer instance per install directory (cross-process lock).
- Add antivirus/EDR exclusions for install and staging directories.
- Monitor free disk space before starting transactions.
- Retry transactions from a freshly verified source after integrity failures.
When it happens
Trigger: Raised in stage_transaction_copy_authenticated when `staged_hash != source_hash` after `io::copy` + flush + sync_all of the source executable into the guarded private destination file. This happens when the source file contents mutate under the open handle during the copy (another process writes to it despite the share lock), copy/flush I/O silently drops or corrupts data (disk full, failing storage), or an FS filter/antivirus rewrites bytes mid-copy.
Common situations: Antivirus or EDR software quarantining or modifying an executable during download/install; another updater process racing to overwrite the same source binary; disk corruption or out-of-space conditions on the target volume; users manually replacing an exe while an update transaction is running.
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
- PermissionDenied
- AlreadyExists
- quarantined capsule authority bytes changed: {}
- leftover capsule authority receipt changed before retirement
- mountpoint must be absolute
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/0c65d42008fb9433.
Report an issue: GitHub.