Skip to content

[Bug]: LSP tools fail on Windows with 'Binary not found' despite correct installation and PATH configuration #1805

@chipfox

Description

@chipfox

[Bug]: LSP tools fail on Windows with "Binary not found" despite correct installation and PATH configuration

Prerequisites

  • I have searched existing issues and discussions to avoid duplicates
  • This bug is specific to oh-my-opencode (not OpenCode core)
  • I have read the documentation
  • I have tested with the latest version (3.5.2)

Bug Description

All LSP tools (lsp_diagnostics, lsp_goto_definition, etc.) fail on Windows with:

Error: [LSP] Binary '{server-name}' not found on Windows. 
Ensure the LSP server is installed and available in PATH.

This occurs even though:

  1. ✅ LSP servers are correctly installed via npm
  2. ✅ LSP servers are in PATH and executable
  3. oh-my-opencode doctor reports LSP servers as installed
  4. ✅ Configuration is valid

Root Cause

The isBinaryAvailableOnWindows() function in src/tools/lsp/lsp-process.ts (lines 24-40, called at line 161) uses spawnSync("where", ...) to verify binary existence.

This check fails because OpenCode's Node.js subprocess doesn't inherit the full PATH environment from the user's shell, even though the same where command works perfectly when run in bash, cmd, PowerShell, or standalone Node.js scripts.

Additionally, this check is redundantisServerInstalled() (from server-installation.ts) already validates binary existence before spawnProcess() is called, and it uses a more reliable PATH traversal method.

Environment

  • oh-my-opencode version: 3.5.2
  • OpenCode version: 1.1.60
  • OS: Windows 11 (10.0.26220.7755)
  • Platform: win32
  • Node.js: v22.15.1
  • Shell: Git Bash (MINGW64)

Steps to Reproduce

  1. Install LSP servers via npm:

    npm install -g yaml-language-server
    npm install -g typescript-language-server
    npm install -g vscode-markdown-language-server
  2. Verify installation:

    where yaml-language-server  # Works - finds binary
    yaml-language-server --stdio  # Works - starts server
  3. Configure oh-my-opencode (already in default config):

    {
      "lsp": {
        "yaml": {
          "command": ["yaml-language-server", "--stdio"],
          "extensions": [".yaml", ".yml"]
        }
      }
    }
  4. Try to use LSP tools in OpenCode:

    lsp_diagnostics(filePath="test.yaml")
    
  5. Expected: Get diagnostics

  6. Actual: Error "Binary 'yaml-language-server' not found on Windows"

Evidence

1. LSP Servers Are Installed and Accessible

$ npm list -g yaml-language-server
C:\Users\[user]\AppData\Roaming\npm
└── [email protected]

$ where yaml-language-server
C:\Users\[user]\AppData\Roaming\npm\yaml-language-server
C:\Users\[user]\AppData\Roaming\npm\yaml-language-server.cmd

$ ls -la /c/Users/[user]/AppData/Roaming/npm/ | grep yaml
-rwxr-xr-x  yaml-language-server
-rw-r--r--  yaml-language-server.cmd
-rwxr-xr-x  yaml-language-server.ps1

2. where Command Works in All Contexts

Tested in:

  • ✅ Git Bash: where yaml-language-server → status 0
  • ✅ cmd.exe: where yaml-language-server → status 0
  • ✅ PowerShell: where.exe yaml-language-server → status 0
  • ✅ Node.js (all spawn options): status 0
// Test with exact oh-my-opencode spawn options
const result = spawnSync("where", ["yaml-language-server"], {
  shell: true,
  windowsHide: true,
  timeout: 5000,
});
console.log(result.status); // 0 (success)

3. Both Detection Functions Work Standalone

Created reproduction script testing both isServerInstalled() and isBinaryAvailableOnWindows():

yaml-language-server:
  isServerInstalled(): true
  isBinaryAvailableOnWindows(): true
  Would LSP work? true

typescript-language-server:
  isServerInstalled(): true
  isBinaryAvailableOnWindows(): true
  Would LSP work? true

Reproduction script: [attached]

4. Bug Affects ALL LSP Servers

Not just yaml-language-server:

lsp_diagnostics("test.yaml")     → "Binary 'yaml-language-server' not found"
lsp_diagnostics("test.md")       → "Binary 'vscode-markdown-language-server' not found"
lsp_diagnostics("test.ts")       → "Binary 'typescript-language-server' not found"

Even though oh-my-opencode doctor reports:

✓ LSP Servers → 2/4 servers available
  • Installed: typescript-language-server
  • Installed: pyright

5. Configuration Is Valid

$ oh-my-opencode doctor
✓ Configuration Validity → Valid JSON config
✓ LSP Servers → 2/4 servers available

# oh-my-opencode.json contains:
{
  "lsp": {
    "yaml": {
      "command": ["yaml-language-server", "--stdio"],
      "extensions": [".yaml", ".yml"]
    },
    "typescript": { ... },
    "markdown": { ... }
  }
}

Proposed Solution

Remove lines 161-166 in src/tools/lsp/lsp-process.ts:

// REMOVE THIS:
if (!isBinaryAvailableOnWindows(cmd)) {
  throw new Error(
    `[LSP] Binary '${cmd}' not found on Windows. ` +
    `Ensure the LSP server is installed and available in PATH. ` +
    `For npm packages, try: npm install -g ${cmd}`
  )
}

Rationale:

  1. Redundant: isServerInstalled() already validated the binary exists earlier in the call chain (before getClient() calls spawnProcess())
  2. More reliable: isServerInstalled() uses PATH traversal with proper extension checking (.cmd, .bat, .ps1)
  3. Unreliable: isBinaryAvailableOnWindows() depends on where command having access to full PATH in subprocess, which varies by environment

Workaround

Users can work around this by using absolute paths in configuration:

{
  "lsp": {
    "yaml": {
      "command": [
        "C:\\Users\\[user]\\AppData\\Roaming\\npm\\yaml-language-server.cmd",
        "--stdio"
      ]
    }
  }
}

This bypasses the where check (line 27 checks for \ and uses existsSync instead).

Additional Context

This is a Windows-specific PATH inheritance issue in subprocess environments. The exact same isBinaryAvailableOnWindows() code works perfectly in standalone Node.js scripts but fails when called from within OpenCode's plugin context.

Related historical issues:

Reproduction Script

#!/usr/bin/env node
const { spawnSync } = require('child_process');
const { existsSync } = require('fs');

// [Full script from reproduce-lsp-bug.js]

Doctor Output

$ oh-my-opencode doctor --verbose

Installation
────────────────────────────────────────
  ✓ OpenCode Installation → 1.1.60
  ✓ Plugin Registration → Registered

Configuration
────────────────────────────────────────
  ✓ Configuration Validity → Valid JSON config
  ✓ Model Resolution → 10 agents, 8 categories

Tools & Servers
────────────────────────────────────────
  ✓ LSP Servers → 2/4 servers available
      • Installed: typescript-language-server
      • Installed: pyright
      • Not found: rust-analyzer (optional)
      • Not found: gopls (optional)

Note: Doctor only checks 4 servers (typescript, pyright, rust, gopls) but doesn't check yaml-language-server, markdown-language-server, etc.


Tested with: oh-my-opencode 3.5.2 (latest as of 2026-02-12)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions