go-delve/delve · error

Could not attach to pid %d: current user does not own the pr

Error message

Could not attach to pid %d: current user does not own the process

What it means

The target process exists but is owned by a different user (uid of /proc/<pid> differs from the current uid); ptrace refuses cross-user attach even with Yama disabled, so the debugger reports the ownership mismatch explicitly.

Source

Thrown at service/debugger/debugger_linux.go:36

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)
			}

			// check if the process is already being traced
			statusfh, err := os.Open(fmt.Sprintf("/proc/%d/status", pid))
			if err != nil {
				return fallbackerr
			}
			defer statusfh.Close()
			scan := bufio.NewScanner(statusfh)
			const tracerPidPrefix = "TracerPid:"
			for scan.Scan() {
				line := scan.Text()
				if strings.HasPrefix(line, tracerPidPrefix) {
					tpid, _ := strconv.Atoi(strings.TrimSpace(line[len(tracerPidPrefix):]))
					if tpid != 0 {
						return fmt.Errorf("could not attach to pid %d: already being debugged by pid %d", pid, tpid)
					}
				}

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Run dlv attach as root (sudo dlv attach <pid>)
  2. Run the target process under your own user account
  3. Attach from inside the container with matching user credentials
  4. Use dlv exec to launch the program under the debugger instead of attaching

Example fix

// before
dlv attach 12345 // process owned by www-data
// after
sudo dlv attach 12345
Defensive patterns

Strategy: validation

Validate before calling

fi, err := os.Stat(fmt.Sprintf("/proc/%d", pid))
if err == nil {
    owned := fi.Sys().(*syscall.Stat_t).Uid == uint32(os.Getuid())
}

Type guard

if st, ok := fi.Sys().(*syscall.Stat_t); ok && st.Uid != uint32(os.Getuid()) { /* not owner */ }

Try / catch

err := debugger.Attach(pid, []string{}, nil)
if err != nil && strings.Contains(err.Error(), "does not own the process") {
    // re-exec attach with sudo or under the owning user
}

Prevention

When it happens

Trigger: 'dlv attach <pid>' where os.Stat("/proc/<pid>") succeeds but the stat Uid differs from os.Getuid(), while EPERM was returned.

Common situations: Attaching to a service running as www-data/systemd user; attaching inside Docker to a process started as root from a non-root shell; shared dev servers.

Related errors


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