Skip to content

JS/TS config file loading in LSP mode can corrupt the LSP protocol stream #20320

@leaysgur

Description

@leaysgur

Summary

When oxlint or oxfmt runs in LSP mode (--lsp) and loads a JS/TS configuration file (e.g., oxlint.config.ts), any console.log() or other stdout output in the config file corrupts the LSP protocol stream, causing the language server to crash.

Root cause

Both oxlint and oxfmt use Node.js import() to evaluate JS/TS config files:

  • oxfmt: apps/oxfmt/src-js/cli/js_config.ts:25await import(fileUrl.href)
  • oxlint: apps/oxlint/src-js/js_config.ts:103await import(fileUrl.href)

In LSP mode, the language server communicates with the client over stdout using the LSP protocol (framed with Content-Length headers). When import() executes a config file that contains console.log() (or any code that writes to process.stdout), the output is written directly to stdout (fd 1), corrupting the LSP message framing.

Reproduction

  1. Create a JS/TS config file (e.g., oxlint.config.ts) with a console.log() call:
    console.log("debug");
    export default { /* config */ };
  2. Open the project in VSCode with the oxc extension installed
  3. The language server crashes immediately with an error like:
    [Error] Client oxc: connection to server is erroring.
    Header must provide a Content-Length property.
    {"\ncontent-length":"292"}
    

The \n before content-length is the trailing newline from console.log() output being injected into the LSP byte stream before the actual Content-Length header.

Affected code paths

  • apps/oxfmt/src-js/cli/js_config.tsloadJsConfig() function (line 25)
  • apps/oxlint/src-js/js_config.tsloadJsConfigs() function (line 103)

Possible fixes

  • Temporarily redirect/suppress stdout while executing import() for config files in LSP mode (e.g., monkey-patch process.stdout.write or reassign fd 1)
  • Load config files in a separate worker/child process with isolated stdio
  • Use a non-import() approach for config loading that doesn't execute arbitrary code

Metadata

Metadata

Assignees

Labels

Type

Priority

None yet

Effort

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions