{"record":{"id":"a6862f9423d4566f","repo":"astrid-runtime/astrid","slug":"layout-migration-label-must-be-non-empty-printab","errorCode":null,"errorMessage":"layout migration {label} must be non-empty printable text","messagePattern":"layout migration (.+?) must be non-empty printable text","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/dirs_layout.rs","lineNumber":69,"sourceCode":"    ///\n    /// # Errors\n    ///\n    /// Returns an error when either durable identity is empty or contains\n    /// control characters that would make operator rendering ambiguous.\n    pub fn new(\n        store_format: impl Into<String>,\n        binary_identity: impl Into<String>,\n    ) -> io::Result<Self> {\n        let target = Self {\n            store_format: store_format.into(),\n            binary_identity: binary_identity.into(),\n        };\n        for (label, value) in [\n            (\"store format\", target.store_format.as_str()),\n            (\"binary identity\", target.binary_identity.as_str()),\n        ] {\n            if value.is_empty() || value.chars().any(char::is_control) {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    format!(\"layout migration {label} must be non-empty printable text\"),\n                ));\n            }\n        }\n        Ok(target)\n    }\n\n    /// Bind a migration to the exact executable bytes performing the cutover.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error when the current executable cannot be resolved or read,\n    /// or when the supplied store-format identity is invalid.\n    pub fn for_current_executable(store_format: impl Into<String>) -> io::Result<Self> {\n        let executable = std::env::current_exe()?;\n        let mut file = File::open(&executable)?;\n        let mut hasher = blake3::Hasher::new_derive_key(\"astrid layout migration binary v1\");","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/dirs_layout.rs#L51-L87","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet target = LayoutMigrationTarget::new(env::var(\"STORE_FORMAT\")?, identity_with_banner)?;\n\n// after\nlet fmt = env::var(\"STORE_FORMAT\")?;\nlet fmt = fmt.trim().to_string();\nif fmt.is_empty() || fmt.chars().any(char::is_control) {\n    return Err(\"STORE_FORMAT must be non-empty printable text\".into());\n}\nlet target = LayoutMigrationTarget::new(fmt, sanitized_identity)?;","handlingStrategy":"validation","validationCode":"// Rust\nfn valid_label(value: &str) -> bool {\n    !value.is_empty() && !value.chars().any(char::is_control)\n}\n// call before construction:\nassert!(valid_label(&store_format) && valid_label(&binary_identity));","typeGuard":"fn is_printable_nonempty(s: &str) -> bool {\n    !s.is_empty() && s.chars().all(|c| !c.is_control())\n}","tryCatchPattern":"match LayoutMigrationTarget::new(fmt, identity) {\n    Err(e) if e.kind() == io::ErrorKind::InvalidInput => {\n        // fall back to default constants and rebuild the target\n    }\n    other => other?,\n}","preventionTips":["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"],"tags":["validation","migration","layout"],"backgroundTag":"empty-required-field","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}