astrid-runtime/astrid · error
layout cutover record is empty: {}
Error message
layout cutover record is empty: {} What it means
A layout cutover record exists and is a regular file, but it is zero bytes long. require_layout_provenance treats an empty record as invalid provenance (InvalidData) because an empty file cannot carry the intent/receipt content that ties the completed v2 ledger to the v1-to-v2 cutover.
Source
Thrown at crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs:226
),
)
} else {
error
}
})?;
if metadata.file_type().is_symlink() || !metadata.is_file() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"layout cutover record is not a regular file: {}",
path.display()
),
));
}
validate_private_entry(&path, &metadata)?;
astrid_core::platform_fs::verify_no_redirects(&path)?;
if metadata.len() == 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("layout cutover record is empty: {}", path.display()),
));
}
}
Ok(())
}
pub(super) fn retire_tree(
path: &Path,
expected: &SourceIdentity,
protected: &[PathBuf],
) -> io::Result<()> {
let actual = snapshot_path(path)?;
if !actual.present {
// A prior post-ledger attempt completed its unlink before a crash.
// Absence is the idempotent terminal state regardless of whether the
// historical source identity was present.View on GitHub (pinned to affd8760f4)
Solutions
- Restore the record's original content from a trusted backup of the migrations directory.
- If content cannot be recovered, redo the v1-to-v2 migration to regenerate both records.
- Never truncate or hand-edit the records; treat them as opaque binary bookkeeping.
- Check for recurring disk-full conditions (quota, small partition) that could truncate writes again.
Example fix
// before > ~/.astrid/migrations/layout-v1-to-v2.complete # wipes the record // after cp backup/migrations/layout-v1-to-v2.complete ~/.astrid/migrations/layout-v1-to-v2.complete
Defensive patterns
Strategy: validation
Validate before calling
use std::fs;
fn records_non_empty(migrations: &std::path::Path) -> std::io::Result<bool> {
for name in ["layout-v1-to-v2.intent", "layout-v1-to-v2.complete"] {
if fs::symlink_metadata(migrations.join(name))?.len() == 0 {
return Ok(false);
}
}
Ok(true)
} Try / catch
match require_layout_provenance(migrations, fresh) {
Err(e) if e.to_string().contains("record is empty") => {
eprintln!("cutover record truncated; restore from backup or rerun migration");
}
other => other?,
} Prevention
- Never open migration records in an editor or redirect output into them ("> file")
- Monitor for disk-full conditions that can truncate record writes
- Verify restored backups with size/hash checks before launching the app
When it happens
Trigger: require_layout_provenance (via reject_incomplete_layout_v2) checks `metadata.len() == 0` after the regular-file, private-permission, and no-redirect checks pass for `layout-v1-to-v2.intent` or `layout-v1-to-v2.complete`. Any truncation of the record to 0 bytes triggers this.
Common situations: Disk-full or crash during record write leaving a truncated file; a text editor opening and saving the record empty; `> file` shell redirection wiping it; backup restore that dropped the file's contents.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- legacy capsule entry has a non-UTF-8 name
- layout cutover record is not a regular file: {}
- legacy source changed before retirement: {}
- legacy retirement root is not a directory: {}
- legacy source is an active mount: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/b6c792ad02884bf2.
Report an issue: GitHub.