rust-lang/rust-analyzer · error

bad kind {other}

Error message

bad kind {other}

What it means

When decoding a flattened token stream over the legacy proc-macro protocol, `SubtreeRepr::read` maps a u32 tag back to a `tt::DelimiterKind`. A value outside 0..=3 is unrecoverable protocol corruption, so the decoder panics with `bad kind {other}`.

Source

Thrown at crates/proc-macro-api/src/legacy_protocol/msg/flat.rs:375

}

impl SubtreeRepr {
    fn write(self) -> [u32; 4] {
        let kind = match self.kind {
            tt::DelimiterKind::Invisible => 0,
            tt::DelimiterKind::Parenthesis => 1,
            tt::DelimiterKind::Brace => 2,
            tt::DelimiterKind::Bracket => 3,
        };
        [self.open.0, kind, self.tt[0], self.tt[1]]
    }
    fn read([open, kind, lo, len]: [u32; 4]) -> SubtreeRepr {
        let kind = match kind {
            0 => tt::DelimiterKind::Invisible,
            1 => tt::DelimiterKind::Parenthesis,
            2 => tt::DelimiterKind::Brace,
            3 => tt::DelimiterKind::Bracket,
            other => panic!("bad kind {other}"),
        };
        SubtreeRepr { open: SpanId(open), close: SpanId(!0), kind, tt: [lo, len] }
    }
    fn write_with_close_span(self) -> [u32; 5] {
        let kind = match self.kind {
            tt::DelimiterKind::Invisible => 0,
            tt::DelimiterKind::Parenthesis => 1,
            tt::DelimiterKind::Brace => 2,
            tt::DelimiterKind::Bracket => 3,
        };
        [self.open.0, self.close.0, kind, self.tt[0], self.tt[1]]
    }
    fn read_with_close_span([open, close, kind, lo, len]: [u32; 5]) -> SubtreeRepr {
        let kind = match kind {
            0 => tt::DelimiterKind::Invisible,
            1 => tt::DelimiterKind::Parenthesis,
            2 => tt::DelimiterKind::Brace,
            3 => tt::DelimiterKind::Bracket,

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Ensure the proc-macro server binary and rust-analyzer use the same version / same legacy protocol encoding
  2. Check the write side (`write`/`write_with_close_span`) emits only tags 0..=3
  3. Validate the flat buffer before decoding, or upgrade both sides to the current (non-legacy) protocol

Example fix

// before (encode)
let kind = if d.kind == tt::DelimiterKind::Invisible { 4 } else { ... };
// after
let kind = match d.kind {
    tt::DelimiterKind::Invisible => 0,
    tt::DelimiterKind::Parenthesis => 1,
    tt::DelimiterKind::Brace => 2,
    tt::DelimiterKind::Bracket => 3,
};
Defensive patterns

Strategy: validation

Validate before calling

fn valid_kind_tag(tag: u32) -> bool { tag <= 3 }

Type guard

fn is_known_delimiter_kind(tag: u32) -> bool {
    matches!(tag, 0..=3)
}

Prevention

When it happens

Trigger: `SubtreeRepr::read` receives a `kind` field other than 0 (Invisible), 1 (Parenthesis), 2 (Brace), or 3 (Bracket) from a serialized 4-word subtree representation.

Common situations: Talking to a proc-macro server built from mismatched rust-analyzer versions whose encoding of delimiter kinds diverged; corrupted or hand-crafted flat token buffers; endianness or offset bugs when reading the flat representation.

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/920d10f5d2af6145. Report an issue: GitHub.