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

  1. Restore the record's original content from a trusted backup of the migrations directory.
  2. If content cannot be recovered, redo the v1-to-v2 migration to regenerate both records.
  3. Never truncate or hand-edit the records; treat them as opaque binary bookkeeping.
  4. 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

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


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