astrid-runtime/astrid · error · io::Error
layout migration source contains a special file: {}
Error message
layout migration source contains a special file: {} What it means
The inventory walk only supports plain files and directories inside the migration source. Any other directory entry type (fifo, socket, device node, etc.) makes the tree impossible to fingerprint deterministically, so inventory_directory aborts with InvalidData naming the path. This keeps the migration inventory well-defined across platforms.
Source
Thrown at crates/astrid-core/src/dirs_layout_records.rs:447
*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(())
}
fn hash_inventory_field(hasher: &mut blake3::Hasher, label: &[u8], value: &[u8]) {
hasher.update(&(label.len() as u64).to_le_bytes());
hasher.update(label);
hasher.update(&(value.len() as u64).to_le_bytes());
hasher.update(value);
}
View on GitHub (pinned to affd8760f4)
Solutions
- Stop the application that created the special file (usually a daemon holding a unix socket) so it is cleaned up.
- Delete the named fifo/socket/device node if it is stale (it will be recreated on next run).
- Verify no device nodes exist: find <state-dir> ! -type f ! -type d and remove/fix the hits.
- Re-run the migration once the tree holds only regular files and directories.
Example fix
// before: stale socket blocks migration $ ls ~/.local/state/astrid/surrealkv.sock # srwxr-xr-x unix socket // after: stop the daemon and remove it $ systemctl --user stop astrid && rm ~/.local/state/astrid/surrealkv.sock && astrid migrate
Defensive patterns
Strategy: validation
Validate before calling
use std::path::Path;
fn assert_only_files_and_dirs(dir: &Path) -> std::io::Result<()> {
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let ft = std::fs::symlink_metadata(entry.path())?.file_type();
if !(ft.is_file() || ft.is_dir()) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("special file in state tree: {}", entry.path().display()),
));
}
if ft.is_dir() {
assert_only_files_and_dirs(&entry.path())?;
}
}
Ok(())
} Type guard
fn is_regular_or_dir(p: &Path) -> bool {
std::fs::symlink_metadata(p)
.map(|m| m.file_type().is_file() || m.file_type().is_dir())
.unwrap_or(false)
} Prevention
- Stop daemons that create unix sockets/fifos inside the state directory before migrating.
- Scan for special files first: find <state-dir> ! -type f ! -type d.
- Keep runtime sockets outside the persisted state tree.
When it happens
Trigger: inventory_directory encounters a child whose symlink_metadata shows it is neither a directory nor a regular file (e.g. a Unix fifo, unix socket, or device node) inside the state tree.
Common situations: A database/runtime left a unix socket file in the state directory; a test or dev script created named pipes there; someone mounted a device node into the tree.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- legacy audit tree contains a special file: {}
- legacy source contains a special entry: {}
- legacy capsule directory is not a regular directory: {}
- legacy capsule entry is not a regular directory: {}
- capsule projection contains a special file: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/76c574c13c527978.
Report an issue: GitHub.