opencode icon indicating copy to clipboard operation
opencode copied to clipboard

Dart LSP fails on Windows: 'Command not found' despite dart being in PATH

Open ravergurl opened this issue 2 weeks ago • 0 comments

Description

The Dart LSP integration fails on Windows with "Command not found: dart" even when:

  1. dart is correctly installed and in PATH
  2. Bun.which('dart') returns a valid executable path
  3. The executable can be spawned successfully

Environment

  • OS: Windows 10/11
  • opencode version: 1.1.1
  • Dart SDK: 3.5.4

Steps to Reproduce

  1. Install Dart SDK on Windows (via Flutter or standalone)
  2. Verify dart --version works in terminal
  3. Run lsp_diagnostics on any .dart file

Expected Behavior

Dart LSP server starts and returns diagnostics.

Actual Behavior

Error: LSP server 'dart' is configured but NOT INSTALLED.

Command not found: dart

To install:
  Included with Dart SDK

Investigation Findings

Verification that dart works:

# Bun.which finds dart correctly
bun -e "console.log(Bun.which('dart'))"
# Output: C:\Users\peopl\bin\dart.exe

# dart runs fine
dart --version
# Output: Dart SDK version: 3.5.4

# Language server works
dart language-server --help
# Output: (help text displayed)

# Spawn without shell works
bun -e "const {spawn} = require('child_process'); const p = spawn(Bun.which('dart'), ['--version']); p.stdout.on('data', d => console.log(d.toString()))"
# Output: Dart SDK version: 3.5.4

Config override doesn't help:

Added to ~/.config/opencode/opencode.jsonc:

"lsp": {
  "dart": {
    "command": ["cmd.exe", "/c", "C:\Users\peopl\bin\dart.exe", "language-server", "--lsp"],
    "extensions": [".dart"]
  }
}

The LSP still reports "Command not found" - the config command override is being ignored during the availability check.

Root Cause Analysis

The issue appears to be in packages/opencode/src/lsp/server.ts. The Dart LSP config does:

export const Dart: Info = {
  id: "dart",
  extensions: [".dart"],
  root: NearestRoot(["pubspec.yaml", "analysis_options.yaml"]),
  async spawn(root) {
    const dart = Bun.which("dart")
    if (!dart) {
      log.info("dart not found, please install dart first")
      return
    }
    return {
      process: spawn(dart, ["language-server", "--lsp"], {
        cwd: root,
      }),
    }
  },
}

Two issues:

  1. The "installed" check happens before config command override is applied
  2. Even when Bun.which("dart") succeeds, something in the availability check fails

Proposed Fix

Option 1: Add shell: true for Windows:

process: spawn(dart, ["language-server", "--lsp"], {
  cwd: root,
  shell: process.platform === "win32",
}),

Option 2: Respect config command override during availability check - if user provides a custom command in config, skip the Bun.which check.

Related

  • PR #4841 added Dart LSP support but didn't handle Windows-specific spawn issues
  • This is similar to issues with .cmd files on Windows requiring shell: true

ravergurl avatar Jan 04 '26 20:01 ravergurl