mihomo-party-org/clash-party · warning

Core PID ${proc.pid ?? 'unknown'} did not exit after SIGINT;

Error message

Core PID ${proc.pid ?? 'unknown'} did not exit after SIGINT; sending SIGKILL

What it means

stopCore sends SIGINT to the core (mihomo) process and waits for it to exit. If the process is still alive after the grace period, this warning is logged and SIGKILL is sent as a forced last resort; if even SIGKILL fails to terminate it, a hard error is thrown. The warning itself means the core ignored or was slow to handle the graceful shutdown signal.

Source

Thrown at src/main/core/manager.ts:872

    cleanupStoppedCoreResources()
  ])
}

setStopCoreBeforeAdminRestart(stopCore)

async function ensureCoreProcessExited(proc: ChildProcess | null): Promise<void> {
  if (!proc) return

  const waitForExit = async (): Promise<boolean> => {
    const deadline = Date.now() + coreShutdownTimeout
    while (proc.exitCode === null && proc.signalCode === null && Date.now() < deadline) {
      await delay(50)
    }
    return proc.exitCode !== null || proc.signalCode !== null
  }

  if (await waitForExit()) return
  managerLogger.warn(`Core PID ${proc.pid ?? 'unknown'} did not exit after SIGINT; sending SIGKILL`)
  proc.kill('SIGKILL')
  if (!(await waitForExit())) {
    throw new Error(`Core PID ${proc.pid ?? 'unknown'} is still running after SIGKILL`)
  }
}

async function restartCoreOnce(forceStop: boolean): Promise<void> {
  const startAttempt = await runCoreOperation(async () => {
    const previousChild = child
    await stopCoreInternal(forceStop)
    if (process.platform === 'darwin') await ensureCoreProcessExited(previousChild)
    return startCoreInternal(false, true)
  })
  await startAttempt.readiness
}

function trackCoreRestart(operation: () => Promise<void>): Promise<void> {
  if (pendingRestart) return pendingRestart

View on GitHub (pinned to 911e090537)

Solutions

  1. Usually self-healing — the SIGKILL path terminates the core; just restart the core/app.
  2. If it recurs, update the core (mihomo) to the latest version where signal handling bugs may be fixed.
  3. Check system state (dmesg/proc status) for processes stuck in uninterruptible I/O and resolve underlying I/O problems.
  4. If 'still running after SIGKILL' errors appear, investigate OS-level issues (zombie children, PID namespace/container quirks) and reboot if needed.

Example fix

// before
managerLogger.warn(`Core PID ${proc.pid ?? 'unknown'} did not exit after SIGINT; sending SIGKILL`)
proc.kill('SIGKILL')
// after
managerLogger.warn(`Core PID ${proc.pid ?? 'unknown'} did not exit after SIGINT; sending SIGKILL`)
proc.kill('SIGKILL')
await trackShutdownFailure(proc) // report recurring SIGKILL reliance to telemetry for core bug triage
Defensive patterns

Strategy: try-catch

Validate before calling

// Before stopping, check the process is still alive and killable:
if (proc.exitCode !== null || proc.signalCode !== null) return // already exited
if (proc.killed) console.warn('Core already signaled; forcing SIGKILL likely')

Try / catch

try {
  await stopCore(proc)
} catch (e) {
  if (e instanceof Error && e.message.includes('still running after SIGKILL')) {
    // OS-level issue: investigate uninterruptible process, log PID for triage
    managerLogger.error(`Core PID ${proc.pid} unkillable — check dmesg / D-state / container PID namespace`, e)
  } else throw e
}

Prevention

When it happens

Trigger: Core is hung (deadlocked, blocked on I/O) and not processing SIGINT; the process is in uninterruptible sleep (D state, e.g. stuck disk/network syscall); the binary ignores SIGINT; extremely slow shutdown exceeding the wait window under heavy load.

Common situations: Core wedged after network interface changes or TUN device issues; low-resource systems where shutdown is slow; core version bugs that mishandle signals; zombie/stuck states after long uptime.

Related errors


AI-assisted analysis of mihomo-party-org/clash-party@911e090537 (2026-08-30). Data as JSON: /api/errors/f15c203fda82308f. Report an issue: GitHub.