astrid-runtime/astrid · error · io::Error
layout migration {label} must be non-empty printable text
Error message
layout migration {label} must be non-empty printable text What it means
LayoutMigrationTarget::new in crates/astrid-core/src/dirs_layout.rs validates that the 'store format' and 'binary identity' labels of a migration target are non-empty strings without control characters. An empty value or one containing control chars (e.g. \\0, \\n, ANSI escapes) fails with ErrorKind::InvalidInput, since these labels are persisted in migration records and must be safe, human-readable text.
Source
Thrown at crates/astrid-core/src/dirs_layout.rs:69
///
/// # Errors
///
/// Returns an error when either durable identity is empty or contains
/// control characters that would make operator rendering ambiguous.
pub fn new(
store_format: impl Into<String>,
binary_identity: impl Into<String>,
) -> io::Result<Self> {
let target = Self {
store_format: store_format.into(),
binary_identity: binary_identity.into(),
};
for (label, value) in [
("store format", target.store_format.as_str()),
("binary identity", target.binary_identity.as_str()),
] {
if value.is_empty() || value.chars().any(char::is_control) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("layout migration {label} must be non-empty printable text"),
));
}
}
Ok(target)
}
/// Bind a migration to the exact executable bytes performing the cutover.
///
/// # Errors
///
/// Returns an error when the current executable cannot be resolved or read,
/// or when the supplied store-format identity is invalid.
pub fn for_current_executable(store_format: impl Into<String>) -> io::Result<Self> {
let executable = std::env::current_exe()?;
let mut file = File::open(&executable)?;
let mut hasher = blake3::Hasher::new_derive_key("astrid layout migration binary v1");View on GitHub (pinned to affd8760f4)
Solutions
- Pass non-empty, printable strings for both store_format and binary_identity.
- Trim and sanitize source values (reject/replace control characters) before constructing the target.
- Provide a hardcoded fallback constant when build metadata is unavailable.
- Validate the upstream config/env source so empty values never reach the constructor.
Example fix
// before
let target = LayoutMigrationTarget::new(env::var("STORE_FORMAT")?, identity_with_banner)?;
// after
let fmt = env::var("STORE_FORMAT")?;
let fmt = fmt.trim().to_string();
if fmt.is_empty() || fmt.chars().any(char::is_control) {
return Err("STORE_FORMAT must be non-empty printable text".into());
}
let target = LayoutMigrationTarget::new(fmt, sanitized_identity)?; Defensive patterns
Strategy: validation
Validate before calling
// Rust
fn valid_label(value: &str) -> bool {
!value.is_empty() && !value.chars().any(char::is_control)
}
// call before construction:
assert!(valid_label(&store_format) && valid_label(&binary_identity)); Type guard
fn is_printable_nonempty(s: &str) -> bool {
!s.is_empty() && s.chars().all(|c| !c.is_control())
} Try / catch
match LayoutMigrationTarget::new(fmt, identity) {
Err(e) if e.kind() == io::ErrorKind::InvalidInput => {
// fall back to default constants and rebuild the target
}
other => other?,
} Prevention
- Trim and control-char-check values sourced from env/config before use
- Use compile-time constants for store format and binary identity
- Never embed multi-line banners or raw bytes in identity fields
- Unit-test target construction with real build metadata
When it happens
Trigger: Constructing LayoutMigrationTarget::new with store_format or binary_identity set to "" or strings containing control characters — typically from unparsed config, raw bytes from a file, or format!-built strings with embedded newlines.
Common situations: Loading store format from an environment variable or config file that is unset/empty; binary identity derived from build metadata that is missing in dev builds; concatenating multi-line version banners into the identity field.
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
- cannot migrate an Astrid home without a layout-version senti
- unsupported Astrid home layout version {version:?}
- layout migration source contains a redirect: {}
- legacy state source is redirected or not a directory: {}
- legacy principal-home root is not a directory: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/a6862f9423d4566f.
Report an issue: GitHub.