docker/compose · error
process with PID %d is still running
Error message
process with PID %d is still running
What it means
pidfile.Write implements single-instance locking: it reads the existing pidfile, and Read returns a non-zero PID only when that PID is currently alive (see the alive(pid) check feeding this path). A live old PID means another instance holds the lock, so Write refuses to overwrite the file. This is intentional mutual exclusion, not corruption.
Source
Thrown at internal/pidfile/pidfile.go:64
if pid != 0 && alive(pid) {
return pid, nil
}
return 0, nil
}
// Write writes a "PID file" at the specified path. It returns an error if the
// file exists and contains a valid PID of a running process, or when failing
// to write the file.
func Write(path string, pid int) error {
if pid < 1 {
return fmt.Errorf("invalid PID (%d): only positive PIDs are allowed", pid)
}
oldPID, err := Read(path)
if err != nil && !os.IsNotExist(err) {
return err
}
if oldPID != 0 {
return fmt.Errorf("process with PID %d is still running", oldPID)
}
return os.WriteFile(path, []byte(strconv.Itoa(pid)), 0o644)
}
View on GitHub (pinned to ddc4b044b6)
Solutions
- Stop the running instance first (kill <oldPID>, or the service's stop command), then start again.
- Confirm the PID is genuinely yours: ps -p <oldPID>; if it is an unrelated process (PID reuse), remove the stale pidfile manually and retry.
- Wire the pidfile path into your init system so restarts handle cleanup (systemd manages this without pidfiles).
- Have your supervisor wait for process exit (e.g. ExecStop/killwait) before restarting.
Example fix
# before $ mydaemon --pidfile /run/app.pid # second instance: process with PID 4242 is still running # after $ kill 4242 && mydaemon --pidfile /run/app.pid # if 4242 is unrelated (PID reuse): rm /run/app.pid && mydaemon --pidfile /run/app.pid
Defensive patterns
Strategy: try-catch
Validate before calling
if oldPID, err := pidfile.Read(path); err == nil && oldPID != 0 {
if syscall.Kill(oldPID, 0) == nil {
return fmt.Errorf("another instance is running as PID %d; stop it first", oldPID)
}
} Type guard
func instanceHoldsLock(path string) (int, bool) {
pid, err := pidfile.Read(path)
return pid, err == nil && pid != 0
} Try / catch
if err := pidfile.Write(path, os.Getpid()); err != nil {
if strings.Contains(err.Error(), "is still running") {
// single-instance lock held: exit cleanly, do not delete the file
return err
}
return err
} Prevention
- Always stop the previous instance before starting a new one.
- Before removing a 'stale' pidfile, verify with ps that the PID is not a reused live process.
- Let systemd/supervisors own lifecycle instead of hand-rolled pidfiles where possible.
When it happens
Trigger: Starting a second instance while the first is running: same --pidfile path, previous process still alive. Also when the old process died but its PID was reused by an unrelated live process (PID-reuse race).
Common situations: docker compose up twice in parallel with the same pidfile; a crashed run left the file and the PID got recycled; daemons/systemd restarting a service before the old one fully exited; stale pidfiles on hosts with fast PID cycling (containers).
Related errors
- invalid PID (%d): only positive PIDs are allowed
- cannot take exclusive lock for project %q: %w
- unsupported protocol for address: %s
- unsupported network: %s
- named pipes are only available on Windows
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/aa7f0d59a252f3aa.
Report an issue: GitHub.