docmirror/dev-sidecar · error

终止占用端口 的进程失败。 PowerShell 方案: CMD 方案

Error message

终止占用端口 ${port} 的进程失败。
PowerShell 方案: ${psError.message}
CMD 方案: ${cmdError.message}

What it means

This is the aggregated failure of kill-by-port on Windows: both the PowerShell plan (Get-NetTCPConnection + Stop-Process) and the CMD plan (netstat -aon parsing + taskkill) failed. The thrown message embeds both underlying error messages (psError and cmdError), so it is the definitive diagnostic for 'could not free the port' on Windows.

Solutions

  1. Read the 'PowerShell 方案:' and 'CMD 方案:' lines in the message to identify which stage failed.
  2. If 'access denied' appears, run DevSidecar (or the taskkill) from an elevated prompt: `taskkill /f /pid <pid> /t`.
  3. Manually identify the owner: `netstat -aon | findstr ":<port>"` then `tasklist /fi "PID eq <pid>"`, and close that application.
  4. If PowerShell execution policy blocks scripts, allow it: `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`.
  5. Reboot or use `Stop-Process -Id <pid> -Force` in an admin PowerShell as a last resort.
Defensive patterns

Strategy: fallback

Validate before calling

const { execSync } = require('child_process')
function findPortOwner(port) {
  try {
    const out = execSync(`powershell -NoProfile -Command "(Get-NetTCPConnection -LocalPort ${port} -State Listen).OwningProcess"`, { encoding: 'utf8' })
    return parseInt(out.trim(), 10) || null
  } catch { return null }
}

Type guard

function hasKillableOwner(port) {
  const pid = findPortOwner(port)
  return Number.isInteger(pid) && pid > 0
}

Try / catch

try {
  await killByPort({ port })
} catch (e) {
  const [psMsg = '', cmdMsg = ''] = e.message.split('\n').filter(l => l.includes('方案'))
  console.error(`kill-by-port failed. PS: ${psMsg} | CMD: ${cmdMsg}`)
  if (/denied|拒绝/i.test(e.message)) throw new Error('elevate privileges and retry')
  if (hasKillableOwner(port)) throw e // process still alive: escalate
  // else port already free — continue
}

Prevention

When it happens

Trigger: Calling the kill-by-port shell script on Windows when: (1) Get-NetTCPConnection is unavailable or the ps invocation itself fails, AND (2) the netstat fallback also fails (empty output / no LISTENING line / taskkill access denied / '未找到处于 LISTENING 状态的进程' from the inner fallback).

Common situations: Port held by a process running as another user or SYSTEM (taskkill denied); pwsh not on PATH on stripped-down Windows installs; antivirus blocking taskkill; port not actually listening at all; corporate policy blocking PowerShell script execution.

Related errors


AI-assisted analysis of docmirror/dev-sidecar@7710cd56cc (2026-08-31). Data as JSON: /api/errors/771f236ead84d763. Report an issue: GitHub.

Appendix: source

Thrown at packages/core/src/shell/scripts/kill-by-port.js:52

        let killed = false
        for (const line of lines) {
          if (!line.includes('LISTENING')) {
            continue
          }
          const parts = line.trim().split(/\s+/)
          const pid = parts[parts.length - 1]
          if (pid && /^\d+$/.test(pid)) {
            await exec([`taskkill /f /pid ${pid} /t`], { type: 'cmd', printErrorLog: false })
            killed = true
          }
        }
        if (!killed) {
          throw new Error('未找到处于 LISTENING 状态的进程')
        }
        return true
      } catch (cmdError) {
        // 两种方案都失败,抛出包含原始错误信息的异常
        throw new Error(
          `终止占用端口 ${port} 的进程失败。\n`
          + `PowerShell 方案: ${psError.message}\n`
          + `CMD 方案: ${cmdError.message}`,
        )
      }
    }
  },

  async linux (exec, { port }) {
    // 主方案:lsof
    try {
      await exec(`kill $(lsof -i:${port} -t 2>/dev/null) 2>/dev/null || true`)
      return true
    } catch (_lsofError) {
      // 备选方案:fuser
      try {
        await exec(`fuser -k ${port}/tcp 2>/dev/null || true`)
        return true

View on GitHub (pinned to 7710cd56cc)