go-delve/delve · error

could not attach to pid %d: %s

Error message

could not attach to pid %d: %s

What it means

Generic fallback message produced by attachErrorMessageLinux in service/debugger/debugger_linux.go when 'dlv attach <pid>' fails with an error that is not a recognized syscall.Errno, or when none of the specific diagnostics (yama ptrace_scope, ownership, TracerPid) apply. It wraps the underlying ptrace/PTRACE_ATTACH error from the native backend.

Source

Thrown at service/debugger/debugger_linux.go:19

package debugger

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
	"syscall"
)

func init() {
	attachErrorMessage = attachErrorMessageLinux
}

//lint:file-ignore ST1005 errors here can be capitalized

func attachErrorMessageLinux(pid int, err error) error {
	fallbackerr := fmt.Errorf("could not attach to pid %d: %s", pid, err)
	if serr, ok := err.(syscall.Errno); ok {
		switch serr {
		case syscall.EPERM:
			// check if ptrace of non-child processes is disabled
			bs, err := os.ReadFile("/proc/sys/kernel/yama/ptrace_scope")
			if err == nil && len(bs) >= 1 && bs[0] != '0' {
				// Yama documentation: https://www.kernel.org/doc/Documentation/security/Yama.txt
				return fmt.Errorf("Could not attach to pid %d: this could be caused by a kernel security setting, try writing \"0\" to /proc/sys/kernel/yama/ptrace_scope", pid)
			}

			// check if the pid belongs to a different process
			fi, err := os.Stat(fmt.Sprintf("/proc/%d", pid))
			if err != nil {
				return fallbackerr
			}
			if fi.Sys().(*syscall.Stat_t).Uid != uint32(os.Getuid()) {
				return fmt.Errorf("Could not attach to pid %d: current user does not own the process", pid)
			}

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Verify the pid exists and is alive: check /proc/<pid> before attaching
  2. Retry attach as root or with CAP_SYS_PTRACE to rule out permission issues
  3. Check the wrapped underlying error text for the real cause
  4. Re-run in the same container/namespace as the target process

Example fix

// before
dlv attach 12345
// after
# confirm target exists first
kill -0 12345 && dlv attach 12345
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := os.Stat(fmt.Sprintf("/proc/%d", pid)); err != nil { return fmt.Errorf("pid %d not alive", pid) }

Type guard

if errno, ok := err.(syscall.Errno); ok { /* handle EPERM/ESRCH specifically */ }

Try / catch

err := debugger.Attach(pid, []string{}, nil)
if err != nil && strings.Contains(err.Error(), "could not attach to pid") {
    // inspect wrapped cause, check /proc/<pid>, retry as root
}

Prevention

When it happens

Trigger: Calling debugger.Attach (or 'dlv attach <pid>') on Linux where the underlying ptrace attach returns an error other than EPERM, or EPERM whose cause is not yama ptrace_scope, wrong-owner, or already-traced.

Common situations: Attaching to a process that already exited between listing and attaching; attaching to a pid in another PID namespace or container; seccomp/LSM policies blocking ptrace; stale pid.

Related errors


AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31). Data as JSON: /api/errors/685c99d7446c6c37. Report an issue: GitHub.