containerd/containerd · warning
ErrNoSuchProcess
ErrNoSuchProcess
Error message
no such process
What it means
ErrNoSuchProcess in pkg/sys/reaper/reaper_unix.go is returned by Wait when the process being waited on no longer exists — its exit was already reaped or it was never a waitable child of the reaper. The reaper subscribes to child exit events (via go-runc exit channels and unix waiting), and Wait uses this sentinel to signal callers that there is nothing to wait for.
Source
Thrown at pkg/sys/reaper/reaper_unix.go:36
package reaper
import (
"errors"
"fmt"
"maps"
"os/exec"
"runtime"
"sync"
"syscall"
"time"
runc "github.com/containerd/go-runc"
"golang.org/x/sys/unix"
)
// ErrNoSuchProcess is returned when the process no longer exists
var ErrNoSuchProcess = errors.New("no such process")
const bufferSize = 32
type subscriber struct {
sync.Mutex
c chan runc.Exit
closed bool
}
func (s *subscriber) close() {
s.Lock()
if s.closed {
s.Unlock()
return
}
close(s.c)
s.closed = true
s.Unlock()View on GitHub (pinned to 4246446a2b)
Solutions
- Wait on each process exactly once; coordinate via the reaper's subscription channel rather than calling Wait redundantly.
- Treat errors.Is(err, reaper.ErrNoSuchProcess) as 'already exited' and look up the exit status you already received.
- Check for goroutines racing to reap the same pid and serialize access.
- Verify you are not waiting on a stale/invalid pid after a failed start.
Example fix
// before: two waiters race on the same pid status, err := reaper.Wait(ctx, pid) // second call -> ErrNoSuchProcess // after: consume the exit event once via subscription exitCh, err := reaper.Subscribe() ... // single Wait on the pid; other code reads the runc.Exit from the channel
Defensive patterns
Strategy: try-catch
Validate before calling
// Check the process is still a waitable child before waiting
if !processExists(pid) {
return ErrAlreadyExited
} Type guard
func isNoSuchProcess(err error) bool {
return errors.Is(err, reaper.ErrNoSuchProcess) || errors.Is(err, unix.ESRCH)
} Try / catch
status, err := reaper.Wait(ctx, pid)
if isNoSuchProcess(err) {
// already reaped elsewhere; use the exit event already received
return lookupRecordedExit(pid), nil
}
return status, err Prevention
- Wait on each pid exactly once; centralize reaping through the reaper.
- Subscribe to exit events instead of double-calling Wait from multiple goroutines.
- Record exit statuses when received so later ErrNoSuchProcess is harmless.
- Guard against pid reuse by keying on pid + start time.
When it happens
Trigger: Calling reaper.Wait on a pid whose exit event was already consumed/delivered to a subscriber, or on a pid that has already been reaped; ECHILD-style conditions during unix wait are mapped to this sentinel.
Common situations: Double-waiting on the same container process from two goroutines; waiting after the reaper already published the exit; pid reuse or stale pid after a crash; racing container exit between the daemon and the reaper.
Related errors
- media type not found
- image name not found in index
- runtime not found in index
- snapshotter not found in index
- streaming api not available
AI-assisted analysis of containerd/containerd@4246446a2b (2026-09-02).
Data as JSON: /api/errors/7efd7105e8d2f7f3.
Report an issue: GitHub.