Skip to content

LSP: a failed server is reported as 'no results' (per-request errors swallowed into an empty array) #12220

Description

@SnowCore8

What happened?

NativeLspService fans every LSP query out over the servers that match the file and collects whatever
hits come back. Each per-server attempt is wrapped in its own try/catch, the catch only writes a
debug log, and the method then falls through to return []. An empty array is also what a successful
query with no hits returns, so callers cannot tell the two apart. When no server can answer — every
attempt threw — the caller is told "no results", and the user sees an empty result instead of a failure.

This is the layer that turned the defect in #12206 into a silent No document symbols found rather than
a visible error: the framing bug made every response unparseable, every request eventually raised, every
catch logged a warning, and documentSymbols returned [] — the same value it returns for a file
that genuinely has no symbols.

Affected methods at main @ 537311b8 (packages/core/src/lsp/native-lsp-service.ts), all sharing the
same shape (per-server catch → debugLogger.warn → fall through to return []):

method start catch return []
workspaceSymbols 979 1102 1110
definitions 1048 1170 1178
documentSymbols 1234 1300 1308
implementations 1314 1371 1379
incomingCalls 1626 1671 1682
outgoingCalls 1688 1733 1744
workspaceDiagnostics 1802 1965 1973

documentSymbols, :1295-1308:

          if (symbols.length > 0) {
            return symbols.slice(0, limit);
          }
        } catch (error) {
          debugLogger.warn(
            `LSP textDocument/documentSymbol failed for ${name}:`,
            error,
          );
        }
      }

      return [];
    }

The single-server, no-hit and all-servers-failed paths therefore converge on one return value, and the
diagnostic goes to debugLogger.warn, which is not part of the user-facing surface. Nothing above this
layer can recover the distinction: the returned [] carries no failure information at all.

What did you expect to happen?

"No results" and "could not ask" should be distinguishable. A query whose servers all failed should
surface as an error (or as a result type that carries the failures), while [] keeps its current meaning
of "at least one server answered, and it found nothing". For the query methods the difference is
usability; for workspaceDiagnostics it is stronger, because [] there asserts "no problems in this
codebase" — a claim that is currently also made when every server errored.

Client information

  • qwen-code version: 0.24.0 (read from /root/.local/lib/qwen-code/package.json)
  • install method: local install under /root/.local — bin wrapper /root/.local/bin/qwen, entry
    scripts/cli-entry.js; the shipped build is an esbuild bundle under lib/chunks
  • node: v24.18.0
  • uname -a:
    Linux FX506LI 6.17.0-35-generic #35~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 19:30:42 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

This is the headless equivalent of /about; the interactive command could not be run here.

Root cause

There is no record of whether any server attempt succeeded. The per-server loop accumulates hits and
breaks out early when it has enough, but on the failure path it only logs, so the method reaches its
final return [] with no way to know that the loop ended because everything threw. The try also wraps
the whole per-server body, so an error raised anywhere inside the attempt — connection failure, request
timeout, malformed response, a rejected promise from the server — lands in the same catch and is
reduced to the same [].

Proposed direction

Decide the all-failed case explicitly rather than letting it fall through: track whether any attempt
completed, and return [] only when at least one server actually answered (with no hits). If every
attempt threw, surface the collected errors — either by throwing, or by widening the return type so the
caller can report "the LSP servers failed" instead of "nothing found". The per-server warn can stay for
the partial-failure case; the change is specifically about the case where nothing succeeded.

This is offered as a direction, not a patch: it changes a return contract that several callers and the
tool layer consume, so the shape is a maintainer call. It was raised here rather than folded into #12206
because that fix is deliberately scoped to the framing defect (PR #12210), as noted in that thread.

Verification

Static source review only — not reproduced. Everything above was read from
packages/core/src/lsp/native-lsp-service.ts at main @ 537311b8, fetched with
gh api "repos/QwenLM/qwen-code/contents/packages/core/src/lsp/native-lsp-service.ts?ref=main". No
executed reproduction of the all-servers-failed path is included, so the claim rests on the code shape
above rather than on observed output. What I did not do: instantiate NativeLspService against a server
stubbed to fail, which is what would show the user-visible empty result end to end.

The finding is corroborated independently: the maintainer analysis in #12206
(#12206) reached the same conclusion while scoping the framing
fix — "Deliberately left out of scope: native-lsp-service.ts collapsing a per-server request failure
into an indistinguishable [], which is what turned this into 'No document symbols found' instead of a
visible error. That is a separate design question — worth its own issue if you want it tracked." This
issue is that follow-up, filed at that invitation.

Related, not duplicate: #3649 (closed) exposed LSP status and startup diagnostics; this report is about
per-request failure being reported as a successful empty result.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    category/coreCore engine and logicpriority/P2Medium - Moderately impactful, noticeable problemscope/corestatus/ready-for-humanSpecified but requires human judgment to implement; not suitable for an autonomous agenttype/bugSomething isn't working as expected

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions