lima-vm/lima · error

failed to kill process with pid %d: %w

Error message

failed to kill process with pid %d: %w

What it means

Lima's usernet network Stop() reads the daemon PID from the pidfile and sends SIGINT via osutil.SysKill. When the kill syscall fails (process no longer exists, permission denied, etc.), the underlying OS error is wrapped with the PID and returned, stopping network shutdown.

Source

Thrown at pkg/networks/usernet/recoincile.go:148

func Stop(ctx context.Context, name string) error {
	logrus.Debugf("Make sure usernet network is stopped")
	pidFile, err := PIDFile(name)
	if err != nil {
		return err
	}
	pid, _ := store.ReadPIDFile(pidFile)

	if pid != 0 {
		logrus.Debugf("Stopping usernet daemon")

		err = writeLeases(ctx, name)
		if err != nil {
			return err
		}

		if err := osutil.SysKill(pid, osutil.SigInt); err != nil {
			logrus.Error(err)
			return fmt.Errorf("failed to kill process with pid %d: %w", pid, err)
		}
	}

	// wait for daemons to terminate (up to 5s) before stopping, otherwise the sockets may not get deleted which
	// will cause subsequent start commands to fail.
	startWaiting := time.Now()
	for {
		if pid, _ := store.ReadPIDFile(pidFile); pid == 0 {
			break
		}
		if time.Since(startWaiting) > 5*time.Second {
			logrus.Infof("usernet network still running after 5 seconds. Attempting to forcibly kill")
			if err := osutil.SysKill(pid, osutil.SigKill); err != nil {
				logrus.Error(err)
			}
			break
		}
		time.Sleep(500 * time.Millisecond)

View on GitHub (pinned to dd909d0973)

Solutions

  1. Verify the daemon is actually running: ps -p <pid>; if it is gone, the error is harmless — remove the stale pidfile under <LIMA_HOME>/networks/user-v2 and retry.
  2. Check process ownership/permissions; run limactl as the same user that started the network, or fix permission to signal the PID.
  3. Delete the stale pidfile (<LIMA_HOME>/_config pidfile path for the network) and re-run limactl start to restart the usernet daemon cleanly.
  4. If SIGINT keeps failing, kill -9 the leftover process manually and clean up sockets in the usernet dir.

Example fix

// before
if err := osutil.SysKill(pid, osutil.SigInt); err != nil {
	return fmt.Errorf("failed to kill process with pid %d: %w", pid, err)
}
// after
if err := osutil.SysKill(pid, osutil.SigInt); err != nil {
	if errors.Is(err, os.ErrProcessDone) {
		return nil // process already exited; stale pidfile
	}
	return fmt.Errorf("failed to kill process with pid %d: %w", pid, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

const pid = fs.readFileSync(path.join(limaHome,'networks/user-v2/<name>/pid'),'utf8').trim();
if (pid) {
  try { process.kill(Number(pid), 0); } catch (e) {
    // stale pidfile: daemon already dead; safe to clean up pidfile and sockets
  }
}

Type guard

function isKillFailure(err) {
  return err != null && /failed to kill process with pid \d+/.test(err.message);
}

Try / catch

try {
  await stopNetwork(name);
} catch (err) {
  if (/failed to kill process with pid/.test(err.message)) {
    // inspect ownership (ESRCH vs EPERM), remove stale pidfile, retry once
  } else throw err;
}

Prevention

When it happens

Trigger: Calling usernet.Stop(ctx, name) (via limactl stop / stopNetwork) when the pidfile contains a PID that has already exited (ESRCH), or when the lima user lacks permission to signal the process (EPERM), or the process is a zombie/owned by another user.

Common situations: Stale PID files left after a crash of the usernet/vde daemon; running limactl as a different user than the one that started the VM; the daemon died between reading the PID file and sending the signal.

Related errors


AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01). Data as JSON: /api/errors/c76824a2e8c3e03b. Report an issue: GitHub.