objectionary/eo-lsp-server

`SemanticTokensProvider` constructor uses non-null assertions on optional `textDocument.semanticTokens` capabilities; crashes minimal `initialize`

Open

#321 opened on May 11, 2026

View on GitHub
 (2 comments) (0 reactions) (0 assignees)TypeScript (9 forks)github user discovery
bughelp wanted

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.

Contributor guide