{"record":{"id":"3e0db8dffdc5cdd5","repo":"go-delve/delve","slug":"wait-err-s-d-3e0db8","errorCode":null,"errorMessage":"wait err %s %d","messagePattern":"wait err (.+?) (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/proc/native/proc_linux.go","lineNumber":456,"sourceCode":")\n\nfunc trapWaitInternal(procgrp *processGroup, pid int, options trapWaitOptions) (*nativeThread, error) {\n\tvar waitdbp *nativeProcess = nil\n\tif len(procgrp.procs) == 1 {\n\t\t// Note that waitdbp is only used to call (*nativeProcess).wait which will\n\t\t// behave correctly if waitdbp == nil.\n\t\twaitdbp = procgrp.procs[0]\n\t}\n\n\thalt := options&trapWaitHalt != 0\n\tfor {\n\t\twopt := 0\n\t\tif options&trapWaitNohang != 0 {\n\t\t\twopt = sys.WNOHANG\n\t\t}\n\t\twpid, status, err := waitdbp.wait(pid, wopt)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"wait err %s %d\", err, pid)\n\t\t}\n\t\tif wpid == 0 {\n\t\t\tif options&trapWaitNohang != 0 {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tdbp := procgrp.procForThread(wpid)\n\t\tvar th *nativeThread\n\t\tif dbp != nil {\n\t\t\tvar ok bool\n\t\t\tth, ok = dbp.threads[wpid]\n\t\t\tif ok {\n\t\t\t\tth.Status = (*waitStatus)(status)\n\t\t\t}\n\t\t} else {\n\t\t\tdbp = procgrp.procs[0]\n\t\t}","sourceCodeStart":438,"sourceCodeEnd":474,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/native/proc_linux.go#L438-L474","documentation":"The wait() system call invoked by trapWaitInternal returned an error while Delve was waiting for a tracee to stop, exit, or signal. trapWait is the core event loop of the native Linux backend: every breakpoint hit, thread exit, and manual stop flows through it, so any wait failure (EINTR races aside) indicates the debugger lost sync with its tracees.","triggerScenarios":"trapWait / trapWaitNohang (via stop or exitGuard): waitdbp.wait(pid, wopt) returns an error — typically ECHILD (all children reaped / process already exited and was collected elsewhere), EINTR propagated without retry, or EINVAL from an options/pid mismatch in __waitpid.","commonSituations":"The debugged process exited concurrently (dlv attach to a process that dies mid-session) yielding ECHILD; signal storms causing repeated EINTR; running under environment wrappers (IDE launches, test harnesses) that interfere with child reaping; multi-process follow-exec sessions where a child was reaped by another thread of the debugger.","solutions":["Check whether the debuggee already exited (dlv will usually report ErrProcessExited on the next command); reconnect or restart the debug session.","Retry the wait: transient EINTR errors usually resolve on the next call.","Ensure only one tracer is attached — other tools (strace, another dlv) reaping tracees cause ECHILD.","If it reproduces in containers, run with --init (proper child reaping, e.g. tini) and PID 1 semantics.","Upgrade Delve: several wait-loop robustness fixes (EINTR handling) have landed over time."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before waiting, confirm the process is still traceable and not reaped\nfunc stillTraced(pid int) error {\n    b, err := os.ReadFile(fmt.Sprintf(\"/proc/%d/status\", pid))\n    if err != nil {\n        return fmt.Errorf(\"process %d gone: %w\", pid, err)\n    }\n    if !strings.Contains(string(b), \"State:\\tT (stopped)\") && !strings.Contains(string(b), \"t (tracing stop)\") {\n        // state may have changed; not fatal, but log it\n        return nil\n    }\n    return nil\n}","typeGuard":"func isWaitErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"wait err \")\n}","tryCatchPattern":"th, err := trapWait(procgrp, pid)\nif err != nil {\n    var exited proc.ErrProcessExited\n    if errors.As(err, &exited) {\n        // debuggee died: clean up and end session gracefully\n        detachAndCleanup()\n        return fmt.Errorf(\"debuggee exited with status %d\", exited.Status)\n    }\n    if isWaitErr(err) && errors.Is(syscall.ECHILD, err) {\n        return fmt.Errorf(\"tracee reaped by another process; check for concurrent strace/dlv\")\n    }\n    return err\n}","preventionTips":["Attach to processes you can monitor — a target dying mid-session is the most common cause","Never run two tracers (dlv, strace, gdb) on the same process simultaneously","In containers use an init process (tini, docker --init) for correct child reaping","Handle ErrProcessExited before interpreting wait errors","Keep Delve up to date for wait-loop EINTR/robustness fixes"],"tags":["linux","waitpid","process-exit","event-loop"],"backgroundTag":"waitpid-failed","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}