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
- Ensure the proc-macro server binary and rust-analyzer use the same version / same legacy protocol encoding
- Check the write side (`write`/`write_with_close_span`) emits only tags 0..=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
- Pin proc-macro server and rust-analyzer to the same version
- Never hand-construct flat protocol buffers; use the writer functions
- Validate tag values (0..=3) before decoding buffers
- Prefer the current protocol over the legacy flat one
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
- bad spacing {other}
- bad tag: {other}
- We explicitly do not provide canonicalization API, as that i
- profiler already started
- profiler failed to start
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/920d10f5d2af6145.
Report an issue: GitHub.