rust-lang/rust-analyzer · error

bad tag: {other}

Error message

bad tag: {other}

What it means

While converting a flat legacy-protocol token stream back into `proc_macro_srv::TokenTree`s, the parser dispatches on a tag word identifying the token-tree variant (group, ident, literal, punctuation). An unrecognized tag means the buffer is corrupt or encoded by an incompatible protocol version; the reader panics with `bad tag: {other}`.

Source

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

                        let text = self.text[repr.text as usize].as_str();
                        let (is_raw, text) = if self.version >= EXTENDED_LEAF_DATA {
                            (
                                if repr.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No },
                                text,
                            )
                        } else {
                            tt::IdentIsRaw::split_from_symbol(text)
                        };
                        s.push(
                            tt::Leaf::Ident(tt::Ident {
                                sym: Symbol::intern(text),
                                span: read_span(repr.id),
                                is_raw,
                            })
                            .into(),
                        )
                    }
                    other => panic!("bad tag: {other}"),
                }
            }
            res[i] = Some((delimiter, s));
        }

        let (delimiter, mut res) = res[0].take().unwrap();
        res.insert(0, tt::TokenTree::Subtree(tt::Subtree { delimiter, len: res.len() as u32 }));
        tt::TopSubtree::from_serialized(res)
    }
}

#[cfg(feature = "in-rust-tree")]
impl<T: SpanTransformer> Reader<'_, T> {
    pub(crate) fn read_tokenstream(
        self,
        span_join: impl Fn(T::Span, T::Span) -> T::Span,
    ) -> proc_macro_srv::TokenStream<T::Span> {
        let mut res: Vec<Option<proc_macro_srv::Group<T::Span>>> = vec![None; self.subtree.len()];

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Rebuild the proc-macro server to match the rust-analyzer/protocol version
  2. Check that buffer offsets are computed with the same word sizes and layout as the writer
  3. Validate all tag values fall in the known set before decoding, or migrate to the current protocol

Example fix

// before
let words = &flat[i..i + 3]; // wrong stride -> reads payload as tag
// after
let words = &flat[i..i + token_tree_word_count(tag)];
assert!(tag <= MAX_KNOWN_TAG, "unknown token tree tag {tag}");
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

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

Prevention

When it happens

Trigger: Decoding a `SubtreeRepr`'s token stream where a token-tree tag word is not one of the known variant discriminators.

Common situations: Proc-macro server built against a different rust-analyzer revision than the client; hand-patched flat buffers; offset arithmetic errors that read a payload word (e.g. a span or length) as a tag.

Related errors


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