{"record":{"id":"1a7563cfb7750ec3","repo":"uutils/coreutils","slug":"invalidutf8-clap-error-for-non-utf-8-argument-valu","errorCode":null,"errorMessage":"InvalidUtf8 clap error for non-UTF-8 argument value","messagePattern":"InvalidUtf8 clap error for non-UTF-8 argument value","errorType":"validation","errorClass":"clap::Error","httpStatus":null,"severity":"error","filePath":"src/uucore/src/lib/features/parser/shortcut_value_parser.rs","lineNumber":99,"sourceCode":"        ContextValue::StyledStrs(vec![format!(\n            \"It looks like '{value}' could match several values. Did you mean {formatted_possible_values}?\"\n        )\n        .into()]),\n    );\n}\n\nimpl TypedValueParser for ShortcutValueParser {\n    type Value = String;\n\n    fn parse_ref(\n        &self,\n        cmd: &clap::Command,\n        arg: Option<&clap::Arg>,\n        value: &std::ffi::OsStr,\n    ) -> Result<Self::Value, clap::Error> {\n        let value = value\n            .to_str()\n            .ok_or(clap::Error::new(ErrorKind::InvalidUtf8))?;\n\n        let matched_values: Vec<_> = self\n            .0\n            .iter()\n            .filter(|x| x.get_name_and_aliases().any(|name| name.starts_with(value)))\n            .collect();\n\n        match matched_values.len() {\n            0 => Err(self.generate_clap_error(cmd, arg, value, &[])),\n            1 => Ok(matched_values[0].get_name().to_string()),\n            _ => {\n                if let Some(direct_match) = matched_values.iter().find(|x| x.get_name() == value) {\n                    Ok(direct_match.get_name().to_string())\n                } else {\n                    Err(self.generate_clap_error(cmd, arg, value, &matched_values))\n                }\n            }\n        }","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/uutils/coreutils/blob/325183372aaf86d8ae6feaf4f9548f74fe815102/src/uucore/src/lib/features/parser/shortcut_value_parser.rs#L81-L117","documentation":"In `ShortcutValueParser::parse_ref`, the input `OsStr` value is converted with `to_str()`; if it isn't valid UTF-8, a clap `ErrorKind::InvalidUtf8` error is raised immediately, since prefix matching against possible values requires a `&str`.","triggerScenarios":"Supplying an argument value containing non-UTF-8 bytes (e.g. from binary data or a filename with invalid encoding) to an option parsed by ShortcutValueParser.","commonSituations":"Shells passing raw bytes from files/commands (`--opt \"$(cat binfile)\"`), locales/encodings producing non-UTF-8 argument bytes, fuzzing.","solutions":["Pass valid UTF-8 values for the option","Re-encode the source data to UTF-8 before passing it","If the option must accept arbitrary bytes, this parser is the wrong choice for that argument"],"exampleFix":"# before\nprog --mode \"$(printf '\\xff\\xfe')\"\n# after\nprog --mode \"fast\"","handlingStrategy":"validation","validationCode":"fn is_valid_utf8(v: &std::ffi::OsStr) -> bool { v.to_str().is_some() }\nif !is_valid_utf8(value) {\n    eprintln!(\"argument value must be valid UTF-8\");\n    std::process::exit(2);\n}","typeGuard":"fn as_utf8(v: &std::ffi::OsStr) -> Option<&str> { v.to_str() }","tryCatchPattern":"match parse_arg(os_value) {\n    Err(e) if e.kind() == clap::error::ErrorKind::InvalidUtf8 => {\n        eprintln!(\"non-UTF-8 value supplied\");\n        clap::Error::new(e.kind()).exit()\n    }\n    other => other?,\n}","preventionTips":["Set a UTF-8 locale in scripts","Never pass raw binary output into CLI options","Fuzz argument parsing with invalid byte sequences"],"tags":["clap","utf-8","argument-parsing"],"backgroundTag":"invalid-utf8-argument","analyzedSha":"325183372aaf86d8ae6feaf4f9548f74fe815102","analyzedAt":"2026-08-31T11:11:36.175Z","contentChangedAt":"2026-08-31T11:11:36.175Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}