rust-lang/rust · critical

non-is_byte literal paired with NonAsciiCharInByte

Error message

non-is_byte literal paired with NonAsciiCharInByte

What it means

Internal invariant in escape-error reporting. `EscapeError::NonAsciiCharInByte` can only originate from byte-mode literals (`b'…'`, `b"…"`, `br"…"`), so the `match mode` arm handling it asserts the mode is one of `Byte`/`ByteStr`/`RawByteStr`. If the literal mode is `Str`, `Char`, or `RawStr`, the (mode, error) pair is inconsistent and the compiler panics — non-ASCII chars are legal in unicode string/char literals.

Source

Thrown at compiler/rustc_parse/src/lexer/unescape_error_reporting.rs:195

                foreign_escape_suggestion(&mut diag, (&ec, span), err_span);
            }
            diag.emit()
        }
        EscapeError::TooShortHexEscape => dcx.emit_err(UnescapeError::TooShortHexEscape(err_span)),
        EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => {
            let (c, span) = last_char();
            let is_hex = error == EscapeError::InvalidCharInHexEscape;
            let ch = escaped_char(c);
            dcx.emit_err(UnescapeError::InvalidCharInEscape { span, is_hex, ch })
        }
        EscapeError::NonAsciiCharInByte => {
            let (c, span) = last_char();
            let desc = match mode {
                Mode::Byte => "byte literal",
                Mode::ByteStr => "byte string literal",
                Mode::RawByteStr => "raw byte string literal",
                _ => panic!("non-is_byte literal paired with NonAsciiCharInByte"),
            };
            let mut err = dcx.struct_span_err(span, format!("non-ASCII character in {desc}"));
            let postfix = if unicode_width::UnicodeWidthChar::width(c).unwrap_or(1) == 0 {
                format!(" but is {c:?}")
            } else {
                String::new()
            };
            err.span_label(span, format!("must be ASCII{postfix}"));
            // Note: the \\xHH suggestions are not given for raw byte string
            // literals, because they are araw and so cannot use any escapes.
            if (c as u32) <= 0xFF && mode != Mode::RawByteStr {
                err.span_suggestion_verbose(
                    span,
                    format!(
                        "if you meant to use the unicode code point for {c:?}, use a \\xHH escape"
                    ),
                    format!("\\x{:X}", c as u32),
                    Applicability::MaybeIncorrect,

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Report as a rustc ICE with the source literal and backtrace
  2. Bisect toolchains to find the regressing commit
  3. As a temporary workaround, change the byte literal to a regular string literal if you can isolate the trigger
Defensive patterns

Strategy: validation

Validate before calling

fn literal_is_ascii_byte_candidate(lit: &str) -> bool {
    // only single ASCII chars belong in b'...' / b"..." literals
    lit.is_ascii() && (lit.len() == 1 || (lit.starts_with('\\') && lit.len() == 2))
}
// Only pair NonAsciiCharInByte diagnostic with literals where is_byte == true
if kind == NonAsciiCharInByte && !literal_is_ascii_byte_candidate(text) { return Ok(()); }

Type guard

pub fn is_byte_literal_text(s: &str) -> bool {
    s.is_ascii() && s.len() == 1
}

Prevention

When it happens

Trigger: The internal escape-error reporting path is invoked with `EscapeError::NonAsciiCharInByte` while passing a non-byte `Mode` (`Str`/`Char`/`RawStr`). Not reachable through well-formed user source.

Common situations: A compiler-internal regression after refactoring literal-mode handling; proc-macro or lint code that re-runs unescape with the wrong mode. Not something end users cause by writing Rust.

Related errors


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/595d1ab25ad2f974.json. Report an issue: GitHub.