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

  1. Ensure the application using the legacy state directory is fully stopped (check for background daemons) before migrating.
  2. Re-run the migration after the directory is idle; the digest will then match.
  3. Check the reported file for unexpected growth (log files, databases being written) and quiesce its writer.
  4. 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

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


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