Skip to content

fix(lsp): fix oxlint --lsp detection regression from Bun→Node migration#16425

Open
zerone0x wants to merge 1 commit intoanomalyco:devfrom
zerone0x:fix/oxlint-lsp-binary-help-detection
Open

fix(lsp): fix oxlint --lsp detection regression from Bun→Node migration#16425
zerone0x wants to merge 1 commit intoanomalyco:devfrom
zerone0x:fix/oxlint-lsp-binary-help-detection

Conversation

@zerone0x
Copy link
Copy Markdown
Contributor

@zerone0x zerone0x commented Mar 7, 2026

Issue for this PR

Closes #16309

Type of change

  • Bug fix

What does this PR do?

OpenCode detects whether an oxlint binary supports --lsp by running oxlint --help and checking for --lsp in the output. This check was broken by the Bun → Node.js process utility migration in 814c1d3.

Root cause: The original Bun code:

const proc = Bun.spawn([lintBin, "--help"], { stdout: "pipe" })
await proc.exited
const help = await readableStreamToText(proc.stdout)

was replaced with:

const proc = Process.spawn([lintBin, "--help"], { stdout: "pipe" })
await proc.exited
if (proc.stdout) {
  const help = await text(proc.stdout)

With Node.js child_process, a piped stdout Readable stream emits end when all data has been written. By the time await proc.exited resolves (process exit event), the stream may have already emitted its end event. Calling text(proc.stdout) afterwards initiates an async iterator that completes immediately on an already-ended stream, returning empty string. The --lsp check fails, the code falls through to looking for oxc_language_server, which is also not installed, and the user sees:

oxlint not found, please install oxlint

…even though node_modules/.bin/oxlint exists and fully supports --lsp.

Fix: Replace the sequential spawn→wait→read pattern with Process.run(), which uses Promise.all([proc.exited, buffer(stdout), buffer(stderr)]) — the correct concurrent pattern already established in the rest of the utility. As a bonus this also checks stderr, covering CLI builds that emit help text there.

The text import from node:stream/consumers is no longer used and is removed.

How did you verify your code works?

  • Traced the regression through git history to the specific commit (814c1d3) that changed readableStreamToTexttext(proc.stdout)
  • Verified Process.run uses Promise.all to read concurrently (correct pattern)
  • Verified Process.RunOptions accepts nothrow: true to handle --help exiting non-zero on some oxlint versions
  • Confirmed the fix matches how other LSP helpers (gopls, rubocop) read process output

Screenshots / recordings

N/A — LSP server startup logic

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

…ation

The oxlint LSP binary detection reads `oxlint --help` output to check
whether the binary supports the `--lsp` flag. The original Bun code used
`readableStreamToText(proc.stdout)` which worked correctly. When migrated
to Node.js in 814c1d3, the pattern became:

    const proc = Process.spawn([lintBin, "--help"], { stdout: "pipe" })
    await proc.exited
    if (proc.stdout) {
      const help = await text(proc.stdout)  // may already be ended

Node.js readable streams emit `end` before `exit` is guaranteed to have
been consumed. By the time `text(proc.stdout)` is called sequentially after
`await proc.exited`, the stream may have already emitted its `end` event,
causing the async iterator inside `text()` to return immediately with empty
content. The `--lsp` check then fails and the code falls through to looking
for the separate `oxc_language_server` binary (also absent in many project
setups), ultimately logging "oxlint not found, please install oxlint" even
though `node_modules/.bin/oxlint` is present and supports `--lsp`.

Fix: use `Process.run()` which reads stdout and stderr concurrently with
the exit via `Promise.all`, matching the correct pattern already used in
the rest of the Process utility. Also fold in stderr so detection works
regardless of which stream the help text is written to.

Fixes anomalyco#16309
@TheRealDevKat
Copy link
Copy Markdown

I just stumbled about that same bug and came to the same conclusion.

I do have one suggestion, instead of a (somewhat brittle) check on the --help output, why not just start the LSP and see if it exists immediately with a non zero exit code?
If it does exit, assume LSP is not supported and fall back to oxc_language_server.
If it doesn't exit, return the process handle.

I don't think parsing the --help output is good practice in the first place. No other LSP checks it like that.

@offlinehacker
Copy link
Copy Markdown

I have implemented a fix here: #21887

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Built-in oxlint LSP checks project dependency but does not use project binary

3 participants