Skip to content

fix(lsp): pyright bunx fallback requests a nonexistent npm package, is unpinned, and never detects a missing bunx #137

Description

@amondnet

Problem

PyrightServer.spawn (packages/lsp/src/server/pyright.ts:18-40) has four problems. Together they mean the bunx fallback never works:

const pyright = Bun.which('pyright-langserver')
if (!pyright) {
  // Try via bunx/npx
  try {
    const proc = spawn('bunx', ['pyright-langserver', '--stdio'], { cwd: root })
    attachLSPProcessHandlers(proc, 'pyright')
    return { process: proc }
  }
  catch (err) {
    log.error({ err }, 'Failed to spawn pyright-langserver via bunx')
    return undefined
  }
}

1. bunx pyright-langserver names a package that does not exist

pyright-langserver is a bin of the npm package pyright, not a package of its own:

Package Latest bin
pyright-langserver — (404) —
pyright 1.1.414 pyright: index.js, pyright-langserver: langserver.index.js

On a machine without Pyright installed, the fallback fails every time. Run with a clean PATH (Bun 1.3.5):

$ bunx pyright-langserver --version
Resolving dependencies
Resolved, downloaded and extracted [1]
error: GET https://registry.npmjs.org/pyright-langserver - 404

The unscoped name is also unclaimed. If anyone ever publishes pyright-langserver, this fallback would download and run their code with --stdio. That is the same failure mode #133 fixed for biome.

Naming the package explicitly works:

$ bunx -p pyright@1.1.414 pyright-langserver --stdio
{"jsonrpc":"2.0","method":"window/logMessage","params":{"type":3,"message":"Pyright language server 1.1.414 starting"}}
…

2. The bunx call is unpinned

The bunx call has no version, unlike the TypeScript fallback (typescript-language-server@${TS_LANGUAGE_SERVER_VERSION}, #131) and the Biome fallback (@biomejs/biome@${BIOME_VERSION}, #134). Pinning keeps the fallback reproducible. As #131 found, a version tag also makes bunx install from the registry instead of reusing a pyright-langserver it finds on PATH.

3. The try/catch around spawn is dead

This is the same bug as #135. spawn from node:child_process reports a missing bunx through an async error event; it does not throw. So when bunx is missing:

  • spawn returns a handle to a process that never started.
  • LSPManager.schedule (packages/lsp/src/index.ts) waits out the initialize timeout instead of marking the server broken.
  • The Failed to spawn … via bunx message never logs.

4. Bun.which('pyright-langserver') doesn't pass the current PATH

Without { PATH: process.env.PATH ?? '' }, Bun searches the copy of PATH it captured at startup. Changes made at runtime are ignored, so the tests can't control the lookup by setting process.env.PATH. Every other server passes PATH since #131 / #134.

Expected

Follow biome.ts (#134) and typescript.ts (#136):

/** pyright version installed by the bunx fallback. */
export const PYRIGHT_VERSION = '1.1.414'

const pyright = Bun.which('pyright-langserver', { PATH: process.env.PATH ?? '' })
if (!pyright) {
  // spawn reports a missing bunx only as an async 'error' event, so check for it first.
  const bunx = Bun.which('bunx', { PATH: process.env.PATH ?? '' })
  if (!bunx) {
    log.warn({ root }, 'pyright not found. Install with: npm install pyright')
    return undefined
  }
  const proc = spawn(bunx, ['-p', `pyright@${PYRIGHT_VERSION}`, 'pyright-langserver', '--stdio'], { cwd: root })
  …
}

Tests

Add a PyrightServer fallback describe in packages/lsp/test/unit/server.test.ts using the shared recorder / spawnServerWith helpers:

  • Falls back to bunx with a pinned pyright: expect bunx -p pyright@1.1.414 pyright-langserver --stdio.
  • Prefers pyright-langserver on PATH over bunx.
  • Returns undefined with the install hint when bunx is missing.

Notes

  • Shim found on PATH: on this machine, Bun.which('pyright-langserver') finds a mise shim (~/.local/share/mise/shims/pyright-langserver) that exits with a mise error. This is the shim case fix(lsp): skip an unrunnable typescript-language-server and fall back to bunx #131 handled for TypeScript with findRunnableBin. Using findRunnableBin here is probably out of scope, because pyright-langserver has no --version flag (it errors with "Connection input stream is not set"), so the version probe doesn't apply as-is.
  • No project-local lookup: unlike TypeScript and Biome, the server never checks the project's node_modules/.bin/pyright-langserver.

Follow-up to #135 / #136.

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

    lsptype:bugSomething isn't working

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions