{"record":{"id":"504040c6cceecc08","repo":"astral-sh/ruff","slug":"invalid-utf-8-in-junit-report","errorCode":null,"errorMessage":"Invalid UTF-8 in JUnit report","messagePattern":"Invalid UTF-8 in JUnit report","errorType":"error_code","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/ruff_db/src/diagnostic/render/junit.rs","lineNumber":177,"sourceCode":"            .entry(filename)\n            .or_insert_with(Vec::new)\n            .push(DiagnosticWithLocation {\n                diagnostic,\n                start_location,\n            });\n    }\n    grouped_diagnostics\n}\n\nstruct FmtAdapter<'a> {\n    fmt: &'a mut dyn std::fmt::Write,\n}\n\nimpl std::io::Write for FmtAdapter<'_> {\n    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {\n        self.fmt\n            .write_str(std::str::from_utf8(buf).map_err(|_| {\n                std::io::Error::new(\n                    std::io::ErrorKind::InvalidData,\n                    \"Invalid UTF-8 in JUnit report\",\n                )\n            })?)\n            .map_err(std::io::Error::other)?;\n\n        Ok(buf.len())\n    }\n\n    fn flush(&mut self) -> std::io::Result<()> {\n        Ok(())\n    }\n\n    fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::io::Result<()> {\n        self.fmt.write_fmt(args).map_err(std::io::Error::other)\n    }\n}\n","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/astral-sh/ruff/blob/26f38c119cac42e4d320ba08f09224fdec74af2c/crates/ruff_db/src/diagnostic/render/junit.rs#L159-L195","documentation":"This io::Error (kind InvalidData) is produced by ruff's JUnit report `FmtAdapter`, a `std::io::Write` adapter that forwards bytes to `fmt::Write`. Because `fmt::Write` only accepts strings, the adapter validates each incoming buffer as UTF-8 and fails with this message if it is not.","triggerScenarios":"Writing bytes that are not valid UTF-8 into a JUnit-format report writer — e.g. `write!` with content derived from non-UTF-8 data, or downstream code that pushes raw byte slices into the adapter.","commonSituations":"Diagnostics containing text decoded from files with non-UTF-8 encodings, lossy conversions upstream, or mixing a byte-level writer API with the string-level JUnit formatter.","solutions":["Ensure all content written to the JUnit report is valid UTF-8 (decode with lossy conversion at the source)","Check where non-UTF-8 bytes enter the report (often a source file read); normalize with `String::from_utf8_lossy`","Route byte output through an explicit encoding step before the JUnit writer"],"exampleFix":"// before\nadapter.write_all(raw_bytes)?; // may contain invalid UTF-8\n// after\nadapter.write_all(String::from_utf8_lossy(raw_bytes).as_bytes())?;","handlingStrategy":"try-catch","validationCode":"if let Err(e) = std::str::from_utf8(buf) {\n    eprintln!(\"non-UTF-8 content for JUnit report: {e}\");\n}\nlet safe = String::from_utf8_lossy(buf);","typeGuard":"fn is_valid_utf8(buf: &[u8]) -> bool {\n    std::str::from_utf8(buf).is_ok()\n}","tryCatchPattern":"match write_result {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {\n        eprintln!(\"JUnit report content was not valid UTF-8\");\n    }\n    Err(e) => return Err(e),\n    Ok(n) => n,\n}","preventionTips":["Decode all external input with from_utf8_lossy before formatting into reports","Keep report writers string-level end to end","Add a test that writes non-ASCII and binary content through the report pipeline"],"tags":["io","encoding","utf8","reporting"],"backgroundTag":"invalid-utf8-argument","analyzedSha":"26f38c119cac42e4d320ba08f09224fdec74af2c","analyzedAt":"2026-09-05T10:32:37.492Z","contentChangedAt":"2026-09-05T10:32:37.492Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}