{"record":{"id":"48e0e8f9b9d767c4","repo":"xai-org/grok-build","slug":"workflow-path-is-not-utf-8","errorCode":null,"errorMessage":"workflow path is not UTF-8","messagePattern":"workflow path is not UTF-8","errorType":"validation","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/session/workflow/store.rs","lineNumber":378,"sourceCode":"            format!(\"immutable workflow file already exists: {}\", path.display()),\n        ));\n    }\n    atomic_write(path, bytes, false)\n}\n\nfn atomic_write_replace(path: &Path, bytes: &[u8]) -> io::Result<()> {\n    atomic_write(path, bytes, true)\n}\n\nfn atomic_write(path: &Path, bytes: &[u8], replace: bool) -> io::Result<()> {\n    let parent = path.parent().ok_or_else(|| {\n        io::Error::new(io::ErrorKind::InvalidInput, \"workflow path has no parent\")\n    })?;\n    std::fs::create_dir_all(parent)?;\n    let file_name = path\n        .file_name()\n        .and_then(|name| name.to_str())\n        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, \"workflow path is not UTF-8\"))?;\n    let tmp = parent.join(format!(\n        \".{file_name}.{}.{}.tmp\",\n        std::process::id(),\n        uuid::Uuid::now_v7().simple()\n    ));\n    let result = (|| {\n        let mut file = std::fs::OpenOptions::new()\n            .write(true)\n            .create_new(true)\n            .open(&tmp)?;\n        file.write_all(bytes)?;\n        file.sync_all()?;\n        drop(file);\n        if !replace && path.exists() {\n            return Err(io::Error::new(\n                io::ErrorKind::AlreadyExists,\n                format!(\"immutable workflow file already exists: {}\", path.display()),\n            ));","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/session/workflow/store.rs#L360-L396","documentation":"atomic_write derives the temp file name from the destination's file_name().to_str(). If the file name is not valid UTF-8 (non-UTF8 bytes on the filesystem), it returns InvalidInput with this message, since the tmp naming scheme requires a str.","triggerScenarios":"Writing an artifact whose path contains non-UTF-8 bytes — e.g. a run directory or file name built from OsString/os platform data with invalid bytes, then passed to atomic_write_new/atomic_write_replace.","commonSituations":"Run ids or directory names imported from external systems with non-UTF-8 encodings; user-supplied names on filesystems allowing arbitrary bytes; concatenating unvalidated OsStr input into workflow paths.","solutions":["Validate that all path components are UTF-8 (and match the run-id charset) before writing","Use validate_run_id for ids so generated paths are always ASCII","Normalize/convert external names with to_string_lossy checks and reject on lossy conversion","Store ids generated internally (uuid simple) rather than raw filesystem names"],"exampleFix":"// before\nlet dir_name = std::env::args_os().nth(1).unwrap(); // may be non-UTF-8\nlet path = runs_root.join(dir_name).join(\"runs.json\");\n// after\nlet dir_name = arg.into_string().map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, \"non-UTF-8 run id\"))?;\nvalidate_run_id(&dir_name)?;\nlet path = runs_root.join(dir_name).join(\"runs.json\");","handlingStrategy":"validation","validationCode":"fn utf8_components(p: &std::path::Path) -> bool {\n    p.components().all(|c| c.as_os_str().to_str().is_some())\n}","typeGuard":"fn as_utf8_path(p: std::path::PathBuf) -> Option<std::path::PathBuf> {\n    if p.components().all(|c| c.as_os_str().to_str().is_some()) { Some(p) } else { None }\n}","tryCatchPattern":"if !utf8_components(&path) {\n    return Err(io::Error::new(io::ErrorKind::InvalidInput, \"workflow path contains non-UTF-8 components\"));\n}\natomic_write(&path, &bytes, true)?;","preventionTips":["Validate run ids with validate_run_id (ASCII-only) before building paths","Convert external OsStr input via into_string() and reject on failure","Avoid os-generated names (readdir results) as workflow path components without revalidation","Standardize on internally generated ASCII ids for all run directories"],"tags":["io","invalid-input","utf-8","path"],"backgroundTag":"non-utf8-path","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}