{"record":{"id":"2235741ec7b17c6b","repo":"astrid-runtime/astrid","slug":"corpus-label-must-contain-only-lowercase-ascii-let","errorCode":null,"errorMessage":"corpus label must contain only lowercase ASCII letters, digits, and hyphens","messagePattern":"corpus label must contain only lowercase ASCII letters, digits, and hyphens","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-chunker-evidence/src/corpus.rs","lineNumber":538,"sourceCode":"        .with_context(|| format!(\"stat corpus input {}\", path.display()))?\n        .len();\n    let mut reader = HashingReader::new(BufReader::with_capacity(READER_CAPACITY, file));\n    std::io::copy(&mut reader, &mut std::io::sink())\n        .with_context(|| format!(\"snapshot corpus input {}\", path.display()))?;\n    let snapshot = reader.finish();\n    if snapshot.logical_bytes != expected_bytes {\n        bail!(\"corpus input changed while its baseline snapshot was captured\");\n    }\n    Ok(snapshot)\n}\n\nfn validate_label(label: &str) -> Result<()> {\n    if label.is_empty()\n        || !label\n            .bytes()\n            .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'-')\n    {\n        bail!(\"corpus label must contain only lowercase ASCII letters, digits, and hyphens\");\n    }\n    Ok(())\n}\n\nfn validate_relative_git_path(path: &Path) -> Result<()> {\n    if path.as_os_str().is_empty()\n        || path.is_absolute()\n        || path.components().any(|component| {\n            matches!(\n                component,\n                std::path::Component::ParentDir\n                    | std::path::Component::RootDir\n                    | std::path::Component::Prefix(_)\n            )\n        })\n        || path.to_string_lossy().contains(':')\n    {\n        bail!(\"git history path must be a relative in-repository path\");","sourceCodeStart":520,"sourceCodeEnd":556,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-chunker-evidence/src/corpus.rs#L520-L556","documentation":"`validate_label` enforces corpus labels be non-empty strings of only lowercase ASCII letters, digits, and hyphens. This keeps labels safe to embed in report filenames and prevents them from smuggling path components.","triggerScenarios":"Creating a `Corpus` via `from_files`/`from_directory` with a name that is empty, contains uppercase letters, underscores, spaces, slashes, or non-ASCII characters.","commonSituations":"Deriving corpus names from filenames that contain uppercase/underscores, using absolute paths as labels, user-supplied CLI names, or names built from `to_string_lossy()` of arbitrary paths.","solutions":["Rename the corpus to a slug: lowercase letters, digits, hyphens only (e.g. `my-corpus-2024`)","Normalize the label before constructing the corpus: `name.to_lowercase().replace([' ', '_'], \"-\")`","Reject or auto-slugify user input in your CLI wrapper before passing it to the corpus builder"],"exampleFix":"// before\nlet corpus = Corpus::from_files(\"My_Corpus/2024\", kind, inputs)?;\n// after\nlet label: String = \"My_Corpus/2024\".to_lowercase().replace([' ', '_', '/'], \"-\");\nlet corpus = Corpus::from_files(&label, kind, inputs)?;","handlingStrategy":"validation","validationCode":"fn is_valid_corpus_label(label: &str) -> bool {\n    !label.is_empty() && label.bytes().all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-')\n}\nassert!(is_valid_corpus_label(&name), \"invalid corpus label: {name}\");","typeGuard":"fn sanitize_label(raw: &str) -> String {\n    let s: String = raw.to_lowercase().bytes().filter(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || *b == b'-').map(|b| b as char).collect();\n    if s.is_empty() { \"corpus\".into() } else { s }\n}","tryCatchPattern":"match Corpus::from_files(&name, kind, inputs) {\n    Ok(c) => c,\n    Err(e) if e.to_string().contains(\"corpus label must contain\") => {\n        let fixed = sanitize_label(&name);\n        Corpus::from_files(&fixed, kind, inputs)?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Slugify any name derived from filenames or paths before use","Disallow uppercase, underscores, spaces, and non-ASCII at the CLI boundary","Add a unit test mirroring validate_label for your label generator"],"tags":["rust","validation","naming","identifier"],"backgroundTag":"invalid-identifier-format","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}