{"record":{"id":"7efd7105e8d2f7f3","repo":"containerd/containerd","slug":"errnosuchprocess","errorCode":"ErrNoSuchProcess","errorMessage":"no such process","messagePattern":"no such process","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/sys/reaper/reaper_unix.go","lineNumber":36,"sourceCode":"\npackage reaper\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"maps\"\n\t\"os/exec\"\n\t\"runtime\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n\n\trunc \"github.com/containerd/go-runc\"\n\t\"golang.org/x/sys/unix\"\n)\n\n// ErrNoSuchProcess is returned when the process no longer exists\nvar ErrNoSuchProcess = errors.New(\"no such process\")\n\nconst bufferSize = 32\n\ntype subscriber struct {\n\tsync.Mutex\n\tc      chan runc.Exit\n\tclosed bool\n}\n\nfunc (s *subscriber) close() {\n\ts.Lock()\n\tif s.closed {\n\t\ts.Unlock()\n\t\treturn\n\t}\n\tclose(s.c)\n\ts.closed = true\n\ts.Unlock()","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/containerd/containerd/blob/4246446a2bf7d03837b0244118d858799393bd80/pkg/sys/reaper/reaper_unix.go#L18-L54","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: two waiters race on the same pid\nstatus, err := reaper.Wait(ctx, pid) // second call -> ErrNoSuchProcess\n// after: consume the exit event once via subscription\nexitCh, err := reaper.Subscribe()\n... // single Wait on the pid; other code reads the runc.Exit from the channel","handlingStrategy":"try-catch","validationCode":"// Check the process is still a waitable child before waiting\nif !processExists(pid) {\n    return ErrAlreadyExited\n}","typeGuard":"func isNoSuchProcess(err error) bool {\n    return errors.Is(err, reaper.ErrNoSuchProcess) || errors.Is(err, unix.ESRCH)\n}","tryCatchPattern":"status, err := reaper.Wait(ctx, pid)\nif isNoSuchProcess(err) {\n    // already reaped elsewhere; use the exit event already received\n    return lookupRecordedExit(pid), nil\n}\nreturn status, err","preventionTips":["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."],"tags":["containerd","process","reaper","no-such-process"],"backgroundTag":"no-such-process","analyzedSha":"4246446a2bf7d03837b0244118d858799393bd80","analyzedAt":"2026-09-02T00:14:43.053Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}