[PR #5317] fix: Windows LSP URIs using backslashes (Biome initialization failure) #11340

Closed
opened 2026-02-16 18:16:09 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/anomalyco/opencode/pull/5317

State: closed
Merged: Yes


Summary

On Windows, the LSP client is currently sending URIs built from Windows paths with backslashes (\). This results in invalid file:// URIs and causes strict LSP servers like Biome to fail during initialize with a URI parsing error (e.g. “unexpected character at index 9”).

LSP requires URIs conforming to RFC 3986, which uses / as the path separator even on Windows (e.g. file:///c:/project/file.ts).

Evidence

{
  "$schema": "https://opencode.ai/config.json",
  "lsp": {
    "typescript":{
      "disabled": true
    },
    "biome": {
      "command": [
        "bunx",
        "@biomejs/biome@latest",
        "lsp-proxy",
        "--stdio"
      ],
      "extensions": [
        ".js",
        ".mjs",
        ".cjs",
        ".jsx",
        ".ts",
        ".mts",
        ".cts",
        ".tsx",
        ".d.ts",
        ".d.mts",
        ".d.cts",
        ".json",
        ".jsonc",
        ".css",
        ".html",
        ".htm",
        ".graphql",
        ".gql",
        ".vue",
        ".svelte",
        ".astro"
      ]
    }
  }
}

Example log when opening a file on Windows:

INFO  2025-12-10T09:55:29 +17ms service=lsp file=.\web\provaH.html touching file
INFO  2025-12-10T09:55:29 +143ms service=lsp serverID=biome spawned lsp server
INFO  2025-12-10T09:55:29 +1ms service=lsp.client serverID=biome starting client
INFO  2025-12-10T09:55:29 +3ms service=lsp.client serverID=biome sending initialize
ERROR 2025-12-10T09:55:30 +1113ms service=lsp.client serverID=biome error=unexpected character at index 9 initialize error
ERROR 2025-12-10T09:55:30 +0ms service=lsp error=LSPInitializeError Caused by: unexpected character at index 9 Failed to initialize LSP client biome
INFO  2025-12-10T09:55:30 +1ms service=default directory=C:\Users\alopez\Desktop\repos\infordisa-portal-nat disposing instance

This matches what you’d expect if the LSP server receives a URI like:

file:///C:\Users\...

instead of:

file:///c:/Users/...

Current Fix

I’ve implemented a change so that, when building URIs from filesystem paths, we now use:

import { pathToFileURL } from "url";

const uri = pathToFileURL(item).href;

This is done to let Node handle the file:// URI normalization (including converting backslashes to forward slashes and properly encoding the path).

On Windows, this fixes the issue in my tests: Biome now initializes correctly and works as expected.

Fix Evidence

INFO  2025-12-10T11:07:15 +1ms service=lsp.client serverID=biome starting client
INFO  2025-12-10T11:07:15 +4ms service=lsp.client serverID=biome sending initialize
INFO  2025-12-10T11:07:15 +9ms service=vcs branch=fix/windows-uri-fix-lsp initialized
INFO  2025-12-10T11:07:15 +0ms service=bus type=file.watcher.updated subscribing
INFO  2025-12-10T11:07:16 +1293ms service=lsp.client serverID=biome initialized
INFO  2025-12-10T11:07:16 +0ms service=bus type=lsp.updated publishing
INFO  2025-12-10T11:07:16 +1ms service=lsp.client serverID=biome path=C:\Users\alopez\Desktop\repos\infordisa-portal-nat\web\provaH.html waiting for diagnostics
INFO  2025-12-10T11:07:16 +0ms service=bus type=lsp.client.diagnostics subscribing
INFO  2025-12-10T11:07:16 +2ms service=lsp.client serverID=biome path=C:\Users\alopez\Desktop\repos\infordisa-portal-nat\web\provaH.html textDocument/didOpen
INFO  2025-12-10T11:07:19 +3007ms service=bus type=lsp.client.diagnostics unsubscribing
{}
INFO  2025-12-10T11:07:19 +1ms service=default directory=C:\Users\alopez\Desktop\repos\opencode\packages\opencode disposing instance
INFO  2025-12-10T11:07:19 +0ms service=state key=C:\Users\alopez\Desktop\repos\opencode\packages\opencode waiting for state disposal to complete
INFO  2025-12-10T11:07:19 +2ms service=lsp.client serverID=biome shutting down
INFO  2025-12-10T11:07:19 +2ms service=lsp.client serverID=biome shutdown

Disclaimer / Request for Review

I’m not very experienced with URI edge cases (UNC paths, non-ASCII, already-encoded paths, etc.), so while pathToFileURL(item).href looks like the correct and simplest solution, I’m not 100% sure there are no side effects on other platforms or setups.
Feel free to offer improvements, comments, or whatever. And do some extra testing before merging this.

**Original Pull Request:** https://github.com/anomalyco/opencode/pull/5317 **State:** closed **Merged:** Yes --- ### Summary On Windows, the LSP client is currently sending URIs built from Windows paths with backslashes (`\`). This results in invalid `file://` URIs and causes strict LSP servers like [Biome](https://github.com/biomejs/biome) to fail during `initialize` with a URI parsing error (e.g. “unexpected character at index 9”). LSP requires URIs conforming to RFC 3986, which uses `/` as the path separator even on Windows (e.g. `file:///c:/project/file.ts`). ### Evidence ```json { "$schema": "https://opencode.ai/config.json", "lsp": { "typescript":{ "disabled": true }, "biome": { "command": [ "bunx", "@biomejs/biome@latest", "lsp-proxy", "--stdio" ], "extensions": [ ".js", ".mjs", ".cjs", ".jsx", ".ts", ".mts", ".cts", ".tsx", ".d.ts", ".d.mts", ".d.cts", ".json", ".jsonc", ".css", ".html", ".htm", ".graphql", ".gql", ".vue", ".svelte", ".astro" ] } } } ``` Example log when opening a file on Windows: ```text INFO 2025-12-10T09:55:29 +17ms service=lsp file=.\web\provaH.html touching file INFO 2025-12-10T09:55:29 +143ms service=lsp serverID=biome spawned lsp server INFO 2025-12-10T09:55:29 +1ms service=lsp.client serverID=biome starting client INFO 2025-12-10T09:55:29 +3ms service=lsp.client serverID=biome sending initialize ERROR 2025-12-10T09:55:30 +1113ms service=lsp.client serverID=biome error=unexpected character at index 9 initialize error ERROR 2025-12-10T09:55:30 +0ms service=lsp error=LSPInitializeError Caused by: unexpected character at index 9 Failed to initialize LSP client biome INFO 2025-12-10T09:55:30 +1ms service=default directory=C:\Users\alopez\Desktop\repos\infordisa-portal-nat disposing instance ``` This matches what you’d expect if the LSP server receives a URI like: ```text file:///C:\Users\... ``` instead of: ```text file:///c:/Users/... ``` ### Current Fix I’ve implemented a change so that, when building URIs from filesystem paths, we now use: ```ts import { pathToFileURL } from "url"; const uri = pathToFileURL(item).href; ``` This is done to let Node handle the `file://` URI normalization (including converting backslashes to forward slashes and properly encoding the path). On Windows, this fixes the issue in my tests: Biome now initializes correctly and works as expected. ### Fix Evidence ```text INFO 2025-12-10T11:07:15 +1ms service=lsp.client serverID=biome starting client INFO 2025-12-10T11:07:15 +4ms service=lsp.client serverID=biome sending initialize INFO 2025-12-10T11:07:15 +9ms service=vcs branch=fix/windows-uri-fix-lsp initialized INFO 2025-12-10T11:07:15 +0ms service=bus type=file.watcher.updated subscribing INFO 2025-12-10T11:07:16 +1293ms service=lsp.client serverID=biome initialized INFO 2025-12-10T11:07:16 +0ms service=bus type=lsp.updated publishing INFO 2025-12-10T11:07:16 +1ms service=lsp.client serverID=biome path=C:\Users\alopez\Desktop\repos\infordisa-portal-nat\web\provaH.html waiting for diagnostics INFO 2025-12-10T11:07:16 +0ms service=bus type=lsp.client.diagnostics subscribing INFO 2025-12-10T11:07:16 +2ms service=lsp.client serverID=biome path=C:\Users\alopez\Desktop\repos\infordisa-portal-nat\web\provaH.html textDocument/didOpen INFO 2025-12-10T11:07:19 +3007ms service=bus type=lsp.client.diagnostics unsubscribing {} INFO 2025-12-10T11:07:19 +1ms service=default directory=C:\Users\alopez\Desktop\repos\opencode\packages\opencode disposing instance INFO 2025-12-10T11:07:19 +0ms service=state key=C:\Users\alopez\Desktop\repos\opencode\packages\opencode waiting for state disposal to complete INFO 2025-12-10T11:07:19 +2ms service=lsp.client serverID=biome shutting down INFO 2025-12-10T11:07:19 +2ms service=lsp.client serverID=biome shutdown ``` ### Disclaimer / Request for Review I’m not very experienced with URI edge cases (UNC paths, non-ASCII, already-encoded paths, etc.), so while `pathToFileURL(item).href` looks like the correct and simplest solution, I’m **not 100% sure** there are no side effects on other platforms or setups. Feel free to offer improvements, comments, or whatever. And do some extra testing before merging this.
yindo added the pull-request label 2026-02-16 18:16:09 -05:00
yindo closed this issue 2026-02-16 18:16:10 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#11340