{"record":{"id":"0e52d30360b9adf4","repo":"go-delve/delve","slug":"could-not-follow-task-across-exec-d","errorCode":null,"errorMessage":"could not follow task across exec: %d\n","messagePattern":"could not follow task across exec: (.+?)\n","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proc/native/proc_darwin.go","lineNumber":355,"sourceCode":"\t\t\t\t// Call trapWait again, it seems\n\t\t\t\t// MACH_RCV_INTERRUPTED is emitted before\n\t\t\t\t// process natural death _sometimes_.\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn nil, nil\n\n\t\tcase 0:\n\t\t\treturn nil, fmt.Errorf(\"error while waiting for task\")\n\t\t}\n\n\t\t// In macOS 10.12.1 if we received a notification for a task other than\n\t\t// the inferior's task and the inferior's task is no longer valid, this\n\t\t// means inferior called execve and its task_t changed.\n\t\tif dbp.os.task != task && C.task_is_valid(dbp.os.task) == 0 {\n\t\t\tdbp.os.task = task\n\t\t\tkret := C.reset_exception_ports(dbp.os.task, &dbp.os.exceptionPort, &dbp.os.notificationPort)\n\t\t\tif kret != C.KERN_SUCCESS {\n\t\t\t\treturn nil, fmt.Errorf(\"could not follow task across exec: %d\\n\", kret)\n\t\t\t}\n\t\t}\n\n\t\t// Since we cannot be notified of new threads on OS X\n\t\t// this is as good a time as any to check for them.\n\t\tdbp.updateThreadList()\n\t\tth, ok := dbp.threads[int(port)]\n\t\tif !ok {\n\t\t\thalt := dbp.os.halt\n\t\t\tif halt {\n\t\t\t\tdbp.os.halt = false\n\t\t\t\treturn th, nil\n\t\t\t}\n\t\t\tif dbp.firstStart || th.singleStepping {\n\t\t\t\tdbp.firstStart = false\n\t\t\t\treturn th, nil\n\t\t\t}\n\t\t\tif err := th.resume(); err != nil {","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/native/proc_darwin.go#L337-L373","documentation":"When the inferior calls execve on macOS its task_t changes. trapWait detects this (notification for a different task while the old one is invalid) and re-acquires the exception/notification ports with reset_exception_ports. If that mach call fails, Delve cannot follow the debuggee across the exec and returns \"could not follow task across exec: <kret>\" with the mach kernel return code.","triggerScenarios":"Debugging a program that calls execve (or a shell script wrapper) on macOS, and reset_exception_ports on the new task returns non-KERN_SUCCESS (invalid task, port rights problem).","commonSituations":"`dlv exec /bin/sh script.sh` where the script execs another binary; Go programs re-executing themselves; setuid or hardened-runtime targets refusing port manipulation.","solutions":["Decode the printed kern_return_t to identify the mach failure (e.g. invalid task/port).","Avoid exec-ing through wrappers; launch the final binary directly with `dlv exec`.","Set breakpoints and use `follow-exec` compatible workflows: continue after the exec instead of stopping inside the wrapper.","Check the new binary isn't SIP-protected or setuid.","Update Delve; exec-following behavior is version-sensitive on macOS."],"exampleFix":"// before\ndlv exec ./run.sh        // script execs ./app\n// after\ngo build -o ./app .\ndlv exec ./app","handlingStrategy":"validation","validationCode":"// Prefer launching the final binary directly over one that will exec:\nfi, err := os.Stat(target)\nif err != nil {\n    return err\n}\nif hasShebangWrapper(fi) { // e.g. script that execs another binary\n    return fmt.Errorf(\"debug the exec'd binary %q directly instead\", actualBinary)\n}","typeGuard":null,"tryCatchPattern":"_, err := dbp.trapWait(-1)\nif err != nil && strings.Contains(err.Error(), \"could not follow task across exec\") {\n    // log the kern_return_t; relaunch pointing at the post-exec binary\n}","preventionTips":["Debug the final binary directly rather than shell wrappers that exec.","Avoid setuid/SIP-protected targets when following exec is needed.","Note the kern_return_t in the message to diagnose the mach failure.","Test exec flows on the exact macOS version in use; behavior varies."],"tags":["darwin","execve","mach","task-port"],"backgroundTag":"execve-task-follow-failed","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}