`SemanticTokensProvider` constructor uses non-null assertions on optional `textDocument.semanticTokens` capabilities; crashes minimal `initialize`
#321 opened on May 11, 2026
Repository metrics
- Stars
- (6 stars)
- PR merge metrics
- (PR metrics pending)
Description
What happens
In src/server.ts, the initialize handler constructs the SemanticTokensProvider by force-asserting two optional client capabilities:
provider = new SemanticTokensProvider(
params.capabilities.textDocument!.semanticTokens!
);
Per LSP 3.17, both ClientCapabilities.textDocument and textDocument.semanticTokens are optional. A conformant minimal client (test harness, editor without semantic-token support, or any custom integration that does not advertise the capability) sends capabilities: {}. Reading .textDocument then yields undefined, and the next .semanticTokens access throws:
TypeError: Cannot read properties of undefined (reading 'semanticTokens')
The provider is constructed unconditionally on line 54, before any hasTokensSupport check happens later in onInitialized (line 108). So the crash happens during the very first round-trip of the LSP lifecycle, before the client has a chance to even register handlers.
What should happen
Either gate the construction behind the existing clientCapsAnalyzer.hasTokensSupport check, or default to an empty capability:
const tokenCaps = params.capabilities.textDocument?.semanticTokens;
if (tokenCaps) {
provider = new SemanticTokensProvider(tokenCaps);
}
And drive the corresponding entries in InitializeResult.capabilities from the same condition. Result: a minimal client that does not advertise semantic-token support can initialize the server successfully, instead of crashing it during initialize.