{"record":{"id":"b0a30997cdffcf38","repo":"AprilNEA/OpenLogi","slug":"fixture-directory-has-no-utf-8-synthetic-id","errorCode":null,"errorMessage":"fixture directory has no UTF-8 synthetic ID","messagePattern":"fixture directory has no UTF-8 synthetic ID","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/openlogi-cli/src/cmd/fixture/contribute.rs","lineNumber":244,"sourceCode":"        args.output.display()\n    );\n    println!(\n        \"No data was uploaded; physical and semantic correctness still require maintainer review.\"\n    );\n    Ok(())\n}\n\nfn publish_manifest_and_finish(\n    directory: &Path,\n    state_path: &Path,\n    profile: &DeviceProfile,\n    cassettes: &[HidCassette],\n    bindings: &[FixtureCaseBinding],\n) -> Result<()> {\n    let fixture_id = directory\n        .file_name()\n        .and_then(OsStr::to_str)\n        .ok_or_else(|| anyhow!(\"fixture directory has no UTF-8 synthetic ID\"))?;\n    let manifest =\n        FixtureManifest::from_assets(fixture_id.to_string(), profile, cassettes, bindings)\n            .context(\"could not generate an exact fixture manifest from sanitized assets\")?;\n    validate_resumable_layout(directory, cassettes)?;\n\n    if !cassettes.is_empty() {\n        let cases_directory = directory.join(CASES_DIRECTORY);\n        if cases_directory\n            .try_exists()\n            .context(\"could not inspect cases directory\")?\n        {\n            require_directory(&cases_directory, \"fixture cases directory\")?;\n        }\n        for cassette in cassettes {\n            let path = cases_directory.join(format!(\"{}.json\", cassette.name));\n            reject_symlink(&path, \"fixture cassette\")?;\n            super::output::write_json_atomically(&path, cassette, true, \"HID cassette\")?;\n        }","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/AprilNEA/OpenLogi/blob/e846e6f4b4405e33bd6a9aaf949a482ce34cb6d8/crates/openlogi-cli/src/cmd/fixture/contribute.rs#L226-L262","documentation":"Thrown by `publish_manifest_and_finish` during fixture contribution when the fixture directory's `file_name()` cannot be converted to a UTF-8 string (`OsStr::to_str` returns None). The directory's final path component doubles as the synthetic fixture ID in the manifest, so a non-UTF-8 name makes an exact, stable fixture ID impossible. This is a filesystem-encoding invariant on the contribution path.","triggerScenarios":"Running the fixture contribute/finish flow against a directory whose final path component contains invalid UTF-8 bytes (possible on Linux where paths are arbitrary bytes, e.g. Latin-1 filenames created by other tools).","commonSituations":"Contributing a fixture directory created or extracted by a tool that wrote non-UTF-8 filenames; a directory name with raw bytes from a zip/tar with a legacy encoding; renaming automation that mangled the name.","solutions":["Rename the fixture directory to a UTF-8 name (ASCII recommended) and re-run the contribute/finish step.","Check the name with `printf '%s' \"$dir\" | xxd | tail` or `ls | LC_ALL=C grep -P '[\\x80-\\xff]'` to spot invalid bytes.","Re-extract or re-create the fixture directory from the source, forcing UTF-8 filenames.","If this comes from an automated pipeline, make it generate fixture IDs from `[A-Za-z0-9_-]` only."],"exampleFix":"// before: non-UTF-8 directory name\n$ openlogi fixture contribute finish /tmp/fixtures/caf$é-latin1\n// error: fixture directory has no UTF-8 synthetic ID\n// after: rename to ASCII UTF-8\n$ mv /tmp/fixtures/caf$'\\xe9'-latin1 /tmp/fixtures/cafe-latin1\n$ openlogi fixture contribute finish /tmp/fixtures/cafe-latin1","handlingStrategy":"validation","validationCode":"// validate the fixture directory name is UTF-8 before starting the contribute flow\nfn validate_fixture_dir(path: &Path) -> Result<(), String> {\n    match path.file_name().and_then(|n| n.to_str()) {\n        Some(name) if name.is_ascii() => Ok(()),\n        _ => Err(format!(\"fixture dir {:?} must have a UTF-8 (ASCII-safe) name\", path)),\n    }\n}","typeGuard":"fn has_utf8_id(path: &std::path::Path) -> bool {\n    path.file_name().map(|n| n.to_str().is_some()).unwrap_or(false)\n}","tryCatchPattern":"match finish(directory) {\n    Err(e) if msg_contains(&e, \"no UTF-8 synthetic ID\") => {\n        eprintln!(\"rename the fixture directory to a UTF-8 name and retry: {e:#}\");\n    }\n    other => other?,\n}","preventionTips":["Create fixture directories with ASCII names only (`[A-Za-z0-9_-]`).","Extract fixture archives with explicit UTF-8 filename handling.","Sanitize directory names in automation before invoking contribute.","On Linux, remember paths are raw bytes — never let external tools name fixture dirs."],"tags":["cli","fixtures","filesystem","encoding"],"backgroundTag":"invalid-identifier-format","analyzedSha":"e846e6f4b4405e33bd6a9aaf949a482ce34cb6d8","analyzedAt":"2026-09-13T03:07:16.451Z","contentChangedAt":"2026-09-13T03:07:16.451Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}