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
- Run dlv attach as root (sudo dlv attach <pid>)
- Run the target process under your own user account
- Attach from inside the container with matching user credentials
- 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
- Verify the target uid matches the debugger uid before attaching
- Run delve under the same user that started the program
- Use sudo or setuid wrapper when attaching to system services
- In containers, exec into the container with a matching uid
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
- could not set options for new traced thread %d %s
- process must be stopped in order to kill it
- failed to remove memlock limit (try running with CAP_SYS_RES
- waiting for target execve failed: %s
- could not attach to new thread %d %s
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/b7d57d2bcbf2f2a1.
Report an issue: GitHub.