neoclide/coc.nvim · warning · Error

Empty symbols returned from language server.

Error message

Empty symbols returned from language server. 

What it means

Thrown when buf.getSymbols() returns undefined/null or an empty array from the language server's textDocument/documentSymbol request. The comment in the source explains this is deliberate: servers may briefly return empty symbol lists right after buffer initialization, so the error forces the tree view to reload rather than rendering a permanently empty outline.

Source

Thrown at src/handler/symbols/outline.ts:198

    let provider = new BasicDataProvider({
      expandLevel: this.config.expandLevel,
      provideData: async () => {
        let buf = this.buffers.getItem(bufnr)
        if (!buf) throw new Error('Document not attached')
        let doc = workspace.getDocument(bufnr)
        if (!languages.hasProvider(ProviderName.DocumentSymbol, doc.textDocument)) {
          throw new Error('Document symbol provider not found')
        }
        let meta = languages.getDocumentSymbolMetadata(doc.textDocument)
        if (meta && meta.label) {
          let views = this.treeViewList.filter(v => v.valid && v.targetBufnr == bufnr)
          views.forEach(view => view.description = meta.label)
        }
        this.setMessage(bufnr, 'Loading document symbols')
        let arr = await buf.getSymbols()
        if (!arr || arr.length == 0) {
          // server may return empty symbols on buffer initialize, throw error to force reload.
          throw new Error('Empty symbols returned from language server. ')
        }
        this.setMessage(bufnr, undefined)
        return this.convertSymbols(bufnr, arr)
      },
      handleClick: async item => {
        let winnr = await nvim.call('bufwinnr', [bufnr])
        if (winnr == -1) return
        nvim.pauseNotification()
        nvim.command(`${winnr}wincmd w`, true)
        let pos = item.selectRange.start
        nvim.call('coc#cursor#move_to', [pos.line, pos.character], true)
        nvim.command(`normal! zz`, true)
        let buf = nvim.createBuffer(bufnr)
        buf.highlightRanges('outline-hover', 'CocHoverRange', [item.selectRange])
        nvim.command('redraw', true)
        await nvim.resumeNotification()
        setTimeout(() => {
          buf.clearNamespace('outline-hover')

View on GitHub (pinned to 50e974d969)

Solutions

  1. Simply reload/refresh the outline — the throw exists to trigger a reload once symbols become available.
  2. Wait for the server to finish indexing (check :CocCommand workspace.showOutput or server status).
  3. Verify the server actually returns symbols (query it with :CocCommand documentSymbols in the same buffer).
  4. If it consistently returns empty, the server lacks documentSymbol support — switch servers or accept a non-outline filetype.
Defensive patterns

Strategy: retry

Try / catch

for (let i = 0; i < 3; i++) {
  try {
    await outline.show(bufnr)
    break
  } catch (e) {
    if (String(e).includes('Empty symbols')) {
      await new Promise(r => setTimeout(r, 500)) // server still indexing
      continue
    }
    throw e
  }
}

Prevention

When it happens

Trigger: Opening :CocOutline while the server is still analyzing the file; the document genuinely has no symbols; the server returns an empty result for documentSymbol (unsupported or an indexing race); the file is empty or contains only comments in some servers.

Common situations: Outline opened immediately after file open on slow servers (rust-analyzer, clangd cold start); huge files not yet fully indexed; servers that return empty instead of null for symbol-less documents; transient server hiccup mid-request.

Related errors


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