astrid-runtime/astrid · error · io::Error
layout migration source changed while inventoried: {}
Error message
layout migration source changed while inventoried: {} What it means
After hashing a file, the inventory compares the number of bytes actually read against the file length captured at scan time (metadata.len()). A mismatch means the file grew, shrank, or was truncated while it was being read, so the computed blake3 digest would not represent a consistent snapshot. The migration aborts with InvalidData to guarantee the inventory digest is trustworthy.
Source
Thrown at crates/astrid-core/src/dirs_layout_records.rs:438
));
}
let mut buffer = vec![0_u8; 64 * 1024].into_boxed_slice();
let mut file_bytes = 0_u64;
loop {
let read = file.read(&mut buffer)?;
if read == 0 {
break;
}
*bytes = bytes
.checked_add(read as u64)
.ok_or_else(|| io::Error::other("layout inventory byte count overflow"))?;
file_bytes = file_bytes
.checked_add(read as u64)
.ok_or_else(|| io::Error::other("layout inventory file length overflow"))?;
hasher.update(&buffer[..read]);
}
if file_bytes != metadata.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"layout migration source changed while inventoried: {}",
child_path.display()
),
));
}
} else {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"layout migration source contains a special file: {}",
child_path.display()
),
));
}
}
Ok(())View on GitHub (pinned to affd8760f4)
Solutions
- Ensure the application using the legacy state directory is fully stopped (check for background daemons) before migrating.
- Re-run the migration after the directory is idle; the digest will then match.
- Check the reported file for unexpected growth (log files, databases being written) and quiesce its writer.
- If corruption is suspected, restore the file from backup and retry.
Example fix
// before: migrating while the app is still running $ astrid migrate # app writes surrealkv mid-hash -> size mismatch // after: stop the app first $ systemctl --user stop astrid && astrid migrate
Defensive patterns
Strategy: retry
Try / catch
match result {
Err(e) if e.to_string().contains("changed while inventoried") => {
// ensure all writers are stopped, then retry once
stop_writers();
run_migration()
}
Err(e) => return Err(e),
Ok(v) => v,
} Prevention
- Stop the owning application (daemons included) before migrating; verify with lsof +D <state-dir>.
- Migrate at startup/shutdown time, not while the app is live.
- Watch for log/state files that grow continuously and relocate them out of the state tree.
When it happens
Trigger: inventory_directory read a file to EOF and the accumulated file_bytes != metadata.len() — the file's size changed between the initial symlink_metadata and the completion of the streaming read.
Common situations: An application still writing logs/state into the legacy directory during migration; a sync client downloading a partially-complete file; a truncated download or disk issue mid-read.
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
- layout migration source changed type: {}
- legacy capsule directory is not a regular directory: {}
- legacy capsule entry is not a regular directory: {}
- legacy capsule {id} changed before retirement
- legacy capsule {id} metadata changed before retirement
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/681669337293b2b7.
Report an issue: GitHub.