Skip to content

[Bug]: LSP server fails to start on Windows when wrapper script (.bat/.cmd) is needed #4262

Description

@KnifeOfLife

Prerequisites

  • I will write this issue in English (see our Language Policy)
  • I have searched existing issues to avoid duplicates
  • I am using the latest version of oh-my-openagent
  • I have read the documentation or asked an AI coding agent with this project's GitHub URL loaded and couldn't find the answer

Bug Description

Bug Description

On Windows, the lsp-tools-mcp module's resolveWindowsCommand function incorrectly prioritizes extensionless files over .bat/.cmd scripts when resolving commands from PATH. This causes LSP servers that rely on wrapper scripts (like jdtls) to fail with exit code -4058 (UV_ENOENT - file not found).

Root Cause

In packages/lsp-tools-mcp/src/lsp/process.ts, the getWindowsPathExtensions function returns extensions with an empty string "" at the beginning:

function getWindowsPathExtensions(env: Record<string, string | undefined>): string[] {
    const rawExtensions = env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD";
    const extensions = rawExtensions
        .split(";")
        .map((extension) => extension.trim())
        .filter(Boolean)
        .map((extension) => (extension.startsWith(".") ? extension : `.${extension}`));
    return [...new Set(["", ...extensions, ".exe", ".cmd", ".bat"])];
    //                     ^ Empty string first!
}

When resolving jdtls in a directory containing both:

  • jdtls (extensionless Python script with shebang)
  • jdtls.bat (wrapper script that sets JAVA_HOME and executes with correct JDK)

The function finds jdtls first (because "" extension is checked before .BAT), returns its full path, and then isWindowsShellShim returns false (since it doesn't end with .bat or .cmd), causing Node.js spawn to execute it directly with shell: false.

The problem: Windows does not support shebang (#!/usr/bin/env python3). An extensionless Python script cannot be executed directly without a shell, resulting in UV_ENOENT error.

Proposed Fix

In packages/lsp-tools-mcp/src/lsp/process.ts, move the empty string "" to the end of the extensions array:

// Before (buggy):
return [...new Set(["", ...extensions, ".exe", ".cmd", ".bat"])];

// After (fixed):
return [...new Set([...extensions, ".exe", ".cmd", ".bat", ""])];

This ensures Windows PATHEXT extensions (.COM, .EXE, .BAT, .CMD) are checked first, matching the behavior of cmd.exe when resolving commands.

Steps to Reproduce

  1. Install jdtls (Eclipse JDT Language Server) on Windows
  2. Configure jdtls with a wrapper script that sets JAVA_HOME to JDK 21:
    • jdtls (extensionless Python script with --java-executable pointing to JDK 21)
    • jdtls.bat (wrapper that sets environment and calls the Python script)
  3. Ensure JDK 8 is the system default (JAVA_HOME points to JDK 8)
  4. Run any LSP operation (e.g., lsp_diagnostics on a .java file)
  5. Observe: LSP server jdtls exited with code -4058

Expected Behavior

The LSP server should start successfully. The command resolution should follow Windows PATHEXT priority order, matching .BAT/.CMD scripts before extensionless files.

Actual Behavior

The LSP server fails to start because the extensionless Python script is executed directly, which Windows cannot handle without shell involvement.

Doctor Output

Opencode version: 1.15.7
oh-my-openagent version: 4.3.0

Error Logs

Configuration

Additional Context

This bug affects any LSP server on Windows that:

  1. Has both an extensionless executable and a .bat/.cmd wrapper
  2. Relies on the wrapper script for correct environment setup

The current behavior contradicts Windows' native command resolution, where PATHEXT extensions are prioritized over extensionless files.

Operating System

Windows

OpenCode Version

1.15.7

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions