{"id":"a8868bdee8e79262","repo":"rust-lang/rust","slug":"failed-to-unescape-byte-literal","errorCode":null,"errorMessage":"failed to unescape byte literal","messagePattern":"failed to unescape byte literal","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler/rustc_ast/src/util/literal.rs","lineNumber":79,"sourceCode":"        let token::Lit { kind, symbol, suffix } = lit;\n        if let Some(suffix) = suffix\n            && !kind.may_have_suffix()\n        {\n            return Err(LitError::InvalidSuffix(suffix));\n        }\n\n        // For byte/char/string literals, chars and escapes have already been\n        // checked in the lexer (in `cook_lexer_literal`). So we can assume all\n        // chars and escapes are valid here.\n        Ok(match kind {\n            token::Bool => {\n                assert!(symbol.is_bool_lit());\n                LitKind::Bool(symbol == kw::True)\n            }\n            token::Byte => {\n                return unescape_byte(symbol.as_str())\n                    .map(LitKind::Byte)\n                    .map_err(|_| panic!(\"failed to unescape byte literal\"));\n            }\n            token::Char => {\n                return unescape_char(symbol.as_str())\n                    .map(LitKind::Char)\n                    .map_err(|_| panic!(\"failed to unescape char literal\"));\n            }\n\n            // There are some valid suffixes for integer and float literals,\n            // so all the handling is done internally.\n            token::Integer => return integer_lit(symbol, suffix),\n            token::Float => return float_lit(symbol, suffix),\n\n            token::Str => {\n                // If there are no characters requiring special treatment we can\n                // reuse the symbol from the token. Otherwise, we must generate a\n                // new symbol because the string in the LitKind is different to the\n                // string in the token.\n                let s = symbol.as_str();","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_ast/src/util/literal.rs#L61-L97","documentation":"Panic in LitKind::from_token_lit when unescape_byte returns an Err for a token::Byte literal. The surrounding comment states the lexer (cook_lexer_literal) is supposed to have already validated all escapes, so reaching this panic indicates the literal token arrived in an invalid state — a broken invariant between lexer and this conversion step. By the time from_token_lit runs, the byte literal is assumed to be well-formed.","triggerScenarios":"Produced when code constructs a token::Byte literal whose symbol contains an invalid escape sequence (e.g. b'\\q') and bypasses lexer validation before calling from_token_lit. Also reachable via fuzzing the lexer/parser boundary or by an internal refactor that lets invalid escapes slip through cook_lexer_literal.","commonSituations":"Nightly rustc regressions; proc-macros that synthesize literal tokens via unstable spans/tokens APIs; fuzz harnesses targeting rustc_lexer. End-user source code with a bad byte literal yields a normal diagnostic long before this panic.","solutions":["File an ICE bug against rust-lang/rust with the exact literal and rustc commit.","If synthesizing literals in a proc-macro or tool, validate/unescape via the public unescape_byte API and avoid emitting malformed token::Byte tokens.","Bisect nightlies to locate the regression in the lexer-to-literal pipeline.","Reduce to the smallest b'...' expression that triggers the panic and attach to the report."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// unescape_byte panics inside from_token_lit when a b'...' literal has an\n// invalid escape. Validate the raw symbol BEFORE handing it to the lexer/lowering.\nuse std::str::FromStr;\nfn valid_byte_literal(sym: &str) -> bool {\n    // Strip the surrounding b'...'\n    let inner = sym.strip_prefix(\"b'\").and_then(|s| s.strip_suffix('\"')).or_else(|| sym.strip_prefix(\"b'\").and_then(|s| s.strip_suffix('\\'')));\n    let Some(inner) = inner else { return false; };\n    rustc_lexer::unescape::unescape_byte(inner).is_ok()\n}\nif !valid_byte_literal(symbol.as_str()) {\n    return Err(format!(\"invalid byte literal: {}\", symbol));\n}","typeGuard":"fn is_valid_byte_lit(kind: token::LitKind, sym: Symbol) -> bool {\n    matches!(kind, token::Byte) && rustc_lexer::unescape::unescape_byte(sym.as_str()).is_ok()\n}","tryCatchPattern":"let lit = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    LitKind::from_token_lit(raw_lit)\n}));\nmatch lit {\n    Ok(Ok(kind)) => kind,\n    Ok(Err(e)) => return Err(format!(\"literal error: {:?}\", e)),\n    Err(_) => return Err(format!(\"byte literal failed to unescape: {}\", raw_lit.symbol)),\n}","preventionTips":["Restrict byte-literal escapes to the legal set: \\\\x00..\\\\x7F, \\\\\\\\, \\\\', \\\\n, \\\\r, \\\\t, \\\\0. Bytes >= 0x80 are not allowed in b'...'.","Never emit raw high-bit bytes or \\u{...} inside a byte literal — use a u8 from_int expression instead.","In codegen that synthesizes literals, prefer constructing LitKind::Byte(value) directly rather than round-tripping through a source symbol that has to be re-unescaped.","Lint generated source for b'...' literals with a regex before feeding it to the compiler."],"tags":["rustc","ice","lexer","literal","byte"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}