GitHub repository with your minimal reproducible example (do not leave this field blank or fill out this field with "github.com/bufbuild/buf" or we will automatically close your issue, see the instructions above!)
https://github.com/garrettr/buf-lsp-symboltag-deprecated-repro
Commands
python3 lsp_probe.py
(`lsp_probe.py` is a dependency-free Python LSP client that opens
`proto/example/v1/example.proto`, requests `textDocument/documentSymbol`,
and prints each returned symbol's `tags` field. It just shells out to
`buf lsp serve`.)
Output
$ python3 lsp_probe.py
buf version: 1.67.0
symbols returned: 9
symbols with SymbolTag.Deprecated (tags=[1]): 9
name=example.v1.Greeting tags=[1]
name=example.v1.Greeting.text tags=[1]
name=example.v1.Greeting.count tags=[1]
name=example.v1.GreeterService tags=[1]
name=example.v1.GreeterService.Greet tags=[1]
name=example.v1.GreetRequest tags=[1]
name=example.v1.GreetRequest.name tags=[1]
name=example.v1.GreetResponse tags=[1]
name=example.v1.GreetResponse.greeting tags=[1]
Expected Output
example.proto contains no option deprecated = true; or [deprecated = true]
anywhere, so no symbol should carry SymbolTag.Deprecated. Expected:
buf version: 1.67.0
symbols returned: 9
symbols with SymbolTag.Deprecated (tags=[1]): 0
name=example.v1.Greeting tags=None
name=example.v1.Greeting.text tags=None
... (etc.)
Anything else?
The user-visible effect is that every symbol in every .proto file is rendered
with a strikethrough in editors that honor SymbolTag.Deprecated from
textDocument/documentSymbol — in VS Code that's the Outline panel, breadcrumbs,
sticky scroll, and Go to Symbol (Cmd+Shift+O). Editor body text is unaffected
because the semantic-token codepath flags deprecated tokens correctly.
Cause. Introduced in #4311 (Add tags to LSP diagnostics, merged
2026-02-03), first released in v1.65.0, still present on main as of this
writing. private/buf/buflsp/symbol.go populates Tags unconditionally:
var isDeprecated bool
if _, ok := s.ir.Deprecated().AsBool(); ok {
isDeprecated = true
}
return protocol.SymbolInformation{
...
Deprecated: isDeprecated,
Tags: []protocol.SymbolTag{ // <-- always set
protocol.SymbolTagDeprecated,
},
}
Per the LSP spec,
clients prefer tags over the deprecated deprecated field, so the
unconditional tag wins. Tags should only include SymbolTagDeprecated when
isDeprecated is true, e.g.:
var tags []protocol.SymbolTag
if isDeprecated {
tags = []protocol.SymbolTag{protocol.SymbolTagDeprecated}
}
A second, smaller bug: the if discards the bool returned by AsBool() and
only checks the second ok return, so isDeprecated (and the legacy
Deprecated field) would also be wrong for an explicit
option deprecated = false;. It should be:
isDeprecated, _ := s.ir.Deprecated().AsBool()
Affected: v1.65.0–v1.69.0 (and main). Last unaffected release: v1.64.0.
Both buf lsp serve and buf beta lsp are affected (they share symbol.go).
Reproduced on Linux x86_64.
GitHub repository with your minimal reproducible example (do not leave this field blank or fill out this field with "github.com/bufbuild/buf" or we will automatically close your issue, see the instructions above!)
https://github.com/garrettr/buf-lsp-symboltag-deprecated-repro
Commands
Output
Expected Output
example.protocontains nooption deprecated = true;or[deprecated = true]anywhere, so no symbol should carry
SymbolTag.Deprecated. Expected:Anything else?
The user-visible effect is that every symbol in every
.protofile is renderedwith a strikethrough in editors that honor
SymbolTag.DeprecatedfromtextDocument/documentSymbol— in VS Code that's the Outline panel, breadcrumbs,sticky scroll, and Go to Symbol (
Cmd+Shift+O). Editor body text is unaffectedbecause the semantic-token codepath flags deprecated tokens correctly.
Cause. Introduced in #4311 (
Add tags to LSP diagnostics, merged2026-02-03), first released in
v1.65.0, still present onmainas of thiswriting.
private/buf/buflsp/symbol.gopopulatesTagsunconditionally:Per the LSP spec,
clients prefer
tagsover the deprecateddeprecatedfield, so theunconditional tag wins.
Tagsshould only includeSymbolTagDeprecatedwhenisDeprecatedis true, e.g.:A second, smaller bug: the
ifdiscards the bool returned byAsBool()andonly checks the second
okreturn, soisDeprecated(and the legacyDeprecatedfield) would also be wrong for an explicitoption deprecated = false;. It should be:Affected:
v1.65.0–v1.69.0(andmain). Last unaffected release:v1.64.0.Both
buf lsp serveandbuf beta lspare affected (they sharesymbol.go).Reproduced on Linux x86_64.