docmirror/dev-sidecar · error

终止占用端口 ${port} 的进程失败。 lsof 方案失败 fuser 方案: ${fuserError.messa

Error message

终止占用端口 ${port} 的进程失败。
lsof 方案失败
fuser 方案: ${fuserError.message}

What it means

On Linux (and macOS, which delegates to the same code), kill-by-port tries `kill $(lsof -i:<port> -t)` first and falls back to `fuser -k <port>/tcp`. This error is thrown only when the fuser fallback itself throws — note both commands end with `|| true` so they mask 'no process found'; a genuine throw means the fuser binary is missing or execution of the shell command failed outright.

Source

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

          + `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
      } catch (fuserError) {
        throw new Error(
          `终止占用端口 ${port} 的进程失败。\n`
          + `lsof 方案失败\n`
          + `fuser 方案: ${fuserError.message}`,
        )
      }
    }
  },

  async mac (exec, { port }) {
    // macOS 与 Linux 采用相同策略
    return executor.linux(exec, { port })
  },
}

module.exports = async function (args) {
  return execute(executor, args)
}

View on GitHub (pinned to 7710cd56cc)

Solutions

  1. Install the fallback tools: Debian/Ubuntu `apt-get install -y psmisc lsof`; Alpine `apk add psmisc lsof`; RHEL `dnf install psmisc lsof`.
  2. Free the port manually: `ss -ltnp 'sport = :<port>'` to find the PID, then `kill -9 <pid>`.
  3. If running in a container, ensure the container has CAP_KILL or perform the kill on the host.
  4. Use `fuser -k -n tcp <port>` directly to confirm the tool works and see stderr without the 2>/dev/null suppression.

Example fix

// before (both tools masked by || true, silent no-op when missing)
await exec(`kill $(lsof -i:${port} -t 2>/dev/null) 2>/dev/null || true`)
// after (verify the tool exists and surface a clear message)
await exec(`command -v lsof >/dev/null || { echo 'lsof not installed'; exit 127; } && kill $(lsof -i:${port} -t)`, { printErrorLog: true })
Defensive patterns

Strategy: fallback

Validate before calling

const { execSync } = require('child_process')
function hasPortTools() {
  for (const t of ['lsof', 'fuser']) {
    try { execSync(`command -v ${t}`) } catch { return { ok: false, missing: t } }
  }
  return { ok: true }
}
// run before kill-by-port on Linux; if missing, install psmisc/lsof or use ss(8)

Type guard

null

Try / catch

try {
  await killByPort({ port })
} catch (e) {
  if (e.message.includes('fuser')) {
    // both lsof and fuser unavailable/failed
    const out = execSync(`ss -ltnp 'sport = :${port}'`, { encoding: 'utf8' })
    const pid = /pid=(\d+)/.exec(out)?.[1]
    if (pid) execSync(`kill -9 ${pid}`)
  } else throw e
}

Prevention

When it happens

Trigger: Calling kill-by-port on Linux when lsof path threw (typically lsof not installed, since the `|| true` suppresses 'no process' cases) AND the fuser fallback also threw (fuser not installed — not part of default minimal images — or exec could not spawn a shell).

Common situations: Alpine/slim Docker containers or minimal distros where neither lsof nor fuser (psmisc) is installed; restricted containers lacking CAP_KILL; shells without /bin/sh fallback.

Related errors


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