Skip to content

Windows: spawn ENOENT, lsof not found, start fails -- three platform bugs #1

@Popoboxxo

Description

@Popoboxxo

Bug 1: spawn('tsx', ...) fails on Windows (ENOENT/EINVAL)

Affected: bin/opencode-pixel-office.js ~lines 438-444 (start command)

Cause: child_process.spawn() cannot execute Unix shebang scripts from node_modules/.bin/ on Windows. The resolved tsx binary (without .cmd extension, line 427-435) is a #!/usr/bin/env node script, which spawn() cannot handle natively on Windows.

Error:

Failed to start server: spawn C:\Users\...\node_modules\.bin\tsx ENOENT

Fix: On Windows, invoke Node.js directly with the tsx loader module instead of spawning the .bin/tsx wrapper:

if (process.platform === "win32") {
  const tsxLoader = path.join(serverCwd, "node_modules", "tsx", "dist", "loader.mjs");
  if (fs.existsSync(tsxLoader)) {
    spawnExe = process.execPath;
    spawnArgs = ["--import", pathToFileURL(tsxLoader).href, serverScript];
  } else {
    // fallback: use tsx.cmd via cmd.exe
    spawnExe = "cmd";
    spawnArgs = ["/c", tsxBin + ".cmd", serverScript];
  }
}

Requires import { pathToFileURL } from "node:url" at top.

Bug 2: lsof does not exist on Windows

Affected: stopServer() (lines 108-123), status command (line 322), start already-running check (line 407)

Cause: execSync('lsof -t -i :${port}') is Unix-only. On Windows, execSync throws, silently caught — but the features are non-functional (can't stop server, can't detect running state).

Fix: Add a cross-platform port-check helper:

const getPidsOnPort = (port) => {
  try {
    if (process.platform === "win32") {
      const output = execSync(
        `powershell -Command "Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess"`,
        { encoding: "utf8" }
      );
      return output.trim().split(/\r?\n/).map(p => parseInt(p)).filter(p => !isNaN(p) && p > 0);
    } else {
      const output = execSync(`lsof -t -i :${port} 2>/dev/null`, { encoding: "utf8" });
      return output.trim().split("\n").map(p => parseInt(p)).filter(p => !isNaN(p) && p > 0);
    }
  } catch { return []; }
};

Then use getPidsOnPort(port) everywhere instead of inline execSync('lsof ...').

Bug 3: execSync('start URL') fails on Windows

Affected: ~lines 411, 459 (opening browser)

Cause: start is a cmd.exe builtin, not a standalone executable. execSync("start http://localhost:5100") throws "Das System kann den angegebenen Pfad nicht finden".

Fix: Use start "" "URL" — the first "" is the required window title argument:

if (process.platform === "win32") {
  execSync(`start "" "${url}"`);
} else if (process.platform === "darwin") {
  execSync(`open "${url}"`);
} else {
  execSync(`xdg-open "${url}"`);
}

Summary

These three bugs make opencode-pixel-office effectively broken on Windows. All fixes are contained in bin/opencode-pixel-office.js and are backward-compatible with Unix.

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