neoclide/coc.nvim · error

Legend must be provided in constructor

Error message

Legend must be provided in constructor

What it means

_push requires a token legend: SemanticTokensBuilder must be constructed with { tokenTypes: [...], tokenModifiers: [...] } (or a default legend). If the builder was created without a legend, string token types cannot be mapped to numeric indices, so it throws this error.

Source

Thrown at src/model/semanticTokensBuilder.ts:79

  public push(range: Range, tokenType: string, tokenModifiers?: string[]): void
  public push(arg0: any, arg1: any, arg2: any, arg3?: any, arg4?: any): void {
    if (typeof arg0 === 'number' && typeof arg1 === 'number' && typeof arg2 === 'number' && typeof arg3 === 'number' && (typeof arg4 === 'number' || typeof arg4 === 'undefined')) {
      if (typeof arg4 === 'undefined') {
        arg4 = 0
      }
      // 1st overload
      return this._pushEncoded(arg0, arg1, arg2, arg3, arg4)
    }
    if (Range.is(arg0) && typeof arg1 === 'string' && isStrArrayOrUndefined(arg2)) {
      // 2nd overload
      return this._push(arg0, arg1, arg2)
    }
    throw new Error('Illegal argument')
  }

  private _push(range: Range, tokenType: string, tokenModifiers?: string[]): void {
    if (!this._hasLegend) {
      throw new Error('Legend must be provided in constructor')
    }
    if (range.start.line !== range.end.line) {
      throw new Error('`range` cannot span multiple lines')
    }
    if (!this._tokenTypeStrToInt.has(tokenType)) {
      throw new Error('`tokenType` is not in the provided legend')
    }
    const line = range.start.line
    const char = range.start.character
    const length = range.end.character - range.start.character
    const nTokenType = this._tokenTypeStrToInt.get(tokenType)!
    let nTokenModifiers = 0
    if (tokenModifiers) {
      for (const tokenModifier of tokenModifiers) {
        if (!this._tokenModifierStrToInt.has(tokenModifier)) {
          throw new Error('`tokenModifier` is not in the provided legend')
        }
        const nTokenModifier = this._tokenModifierStrToInt.get(tokenModifier)!

View on GitHub (pinned to 50e974d969)

Solutions

  1. Construct the builder with a legend: new SemanticTokensBuilder({ tokenTypes: ['keyword','string',...], tokenModifiers: ['readonly',...] })
  2. Or supply the same tokenTypes/tokenModifiers arrays your server declares in its SemanticTokensLegend
  3. Use the encoded overload (numeric tokenType) if you truly have no legend

Example fix

// before
const builder = new SemanticTokensBuilder()
// after
const builder = new SemanticTokensBuilder({ tokenTypes: ['keyword','string','number'], tokenModifiers: ['readonly','deprecated'] })
Defensive patterns

Strategy: validation

Validate before calling

if (!builder || !legend || !legend.tokenTypes?.length) {
  throw new Error('SemanticTokensBuilder requires a legend')
}

Type guard

function hasLegend(b: SemanticTokensBuilder): boolean {
  return (b as any).legend !== undefined || (b as any)._hasLegend === true
}

Try / catch

try {
  builder.push(range, tokenType)
} catch (e) {
  if (e.message.includes('Legend must be provided')) {
    builder = new SemanticTokensBuilder({ tokenTypes: serverLegend.tokenTypes, tokenModifiers: serverLegend.tokenModifiers })
  }
}

Prevention

When it happens

Trigger: Creating new SemanticTokensBuilder() with no legend and then calling push(range, 'keyword', ...) — the Range/string overload always requires a legend to translate the string tokenType.

Common situations: Following older examples that used the default vscode legend; forgetting the constructor argument when wiring a custom semantic tokens provider; upgrading coc.nvim where legend became required.

Related errors


AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31). Data as JSON: /api/errors/82e37d1da8af7214. Report an issue: GitHub.