{"record":{"id":"ceba4154bbf24b09","repo":"go-delve/delve","slug":"s-ceba41","errorCode":null,"errorMessage":"%s","messagePattern":"%s","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proc/target_group.go","lineNumber":246,"sourceCode":"\treturn r\n}\n\n// Detach detaches all targets in the group.\nfunc (grp *TargetGroup) Detach(kill bool) error {\n\tvar errs []string\n\tfor i := len(grp.targets) - 1; i >= 0; i-- {\n\t\tt := grp.targets[i]\n\t\tisvalid, _ := t.Valid()\n\t\tif !isvalid {\n\t\t\tcontinue\n\t\t}\n\t\terr := grp.detachTarget(t, kill)\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Sprintf(\"could not detach process %d: %v\", t.Pid(), err))\n\t\t}\n\t}\n\tif len(errs) > 0 {\n\t\treturn fmt.Errorf(\"%s\", strings.Join(errs, \"\\n\"))\n\t}\n\treturn grp.procgrp.Close()\n}\n\n// detachTarget will detach the target from the underlying process.\n// This means the debugger will no longer receive events from the process\n// we were previously debugging.\n// If kill is true then the process will be killed when we detach.\nfunc (grp *TargetGroup) detachTarget(t *Target, kill bool) error {\n\tif !kill {\n\t\tif t.asyncPreemptChanged {\n\t\t\tsetAsyncPreemptOff(t, t.asyncPreemptOff)\n\t\t}\n\t\tfor _, bp := range t.Breakpoints().M {\n\t\t\tif bp != nil {\n\t\t\t\terr := t.ClearBreakpoint(bp.Addr)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/target_group.go#L228-L264","documentation":"TargetGroup.Detach aggregates per-process detach failures and returns them joined as a single multi-line error via fmt.Errorf(\"%s\", ...). The debugger failed to detach one or more live target processes (or their procgrp.Close() cleanup was skipped because errors occurred). Each line reads 'could not detach process <pid>: <reason>'.","triggerScenarios":"Calling Detach(kill) (via debugger.Detach, used by TestUnsupportedArch, TestAttachDetach, TestWaitForAttach) when detachTarget fails: clearing a breakpoint fails because process memory is unreadable, restoring async-preempt state fails, or procgrp.Detach fails (process already exited, ptrace PTRACE_DETACH denied, thread in uninterruptible state).","commonSituations":"Target process crashed or exited before detach; tracing permissions lost (ptrace scope); kernel refuses PTRACE_DETACH while a thread is stopped at a signal; breakpoint clearing hits unmapped memory after exec changes; zombie processes in the group.","solutions":["Inspect the per-pid lines in the error to see which process failed and why","Verify the target process still exists and is traceable (check /proc/<pid>/status, TracerPid)","Clear all breakpoints/restore state before calling Detach, or retry Detach shortly after resuming","If the process is a zombie or already dead, it can be removed from the group or ignored","As a last resort use Detach(true) to kill the process instead of resuming it"],"exampleFix":"// before\nif err := dlvClient.Detach(false); err != nil { return err } // fails: process already exited\n// after\nif st, _ := client.State(); st.Exited {\n    // process gone: nothing to detach gracefully, close connection instead\n    return client.Detach(true) // or handle exit before detaching\n}\nreturn client.Detach(false)","handlingStrategy":"try-catch","validationCode":"ok, err := grp.Valid()\nif !ok {\n    // no live target to detach; skip or handle err (usually process exited)\n    return nil\n}","typeGuard":"func isDetachErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"could not detach process\")\n}","tryCatchPattern":"if err := grp.Detach(false); err != nil {\n    for _, line := range strings.Split(err.Error(), \"\\n\") {\n        var pid int\n        if n, _ := fmt.Sscanf(line, \"could not detach process %d\", &pid); n == 1 {\n            log.Printf(\"detach failed for pid %d: %s\", pid, line)\n        }\n    }\n}","preventionTips":["Check target validity (grp.Valid() / client.State) before detaching","Resume or handle stopped threads before detach; kernel refuses PTRACE_DETACH on signal-stopped threads","Verify the process still exists via /proc/<pid> before calling Detach","Prefer kill=true when the debuggee is known-bad so cleanup failures do not leave zombies"],"tags":["go","debugger","detach","ptrace"],"backgroundTag":"detach-failed","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}