go-delve/delve · error

could not read proc stat: %v

Error message

could not read proc stat: %v

What it means

During initialize on Linux, delve reads the target's command name (comm) from /proc/<pid>/comm; when comm is empty it falls back to parsing /proc/<pid>/stat. If os.ReadFile of that stat file fails, delve returns 'could not read proc stat'. It needs the process name to populate thread/cmd metadata.

Source

Thrown at pkg/proc/native/proc_linux.go:279

		log.Debugf("waitfor: new process %q", string(buf))
		if strings.HasPrefix(string(buf), pfx) {
			return pid, nil
		}
	}
	return 0, nil
}

func initialize(dbp *nativeProcess) (string, error) {
	comm, err := os.ReadFile(fmt.Sprintf("/proc/%d/comm", dbp.pid))
	if err == nil {
		// removes newline character
		comm = bytes.TrimSuffix(comm, []byte("\n"))
	}

	if len(comm) <= 0 {
		stat, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", dbp.pid))
		if err != nil {
			return "", fmt.Errorf("could not read proc stat: %v", err)
		}
		expr := fmt.Sprintf("%d\\s*\\((.*)\\)", dbp.pid)
		rexp, err := regexp.Compile(expr)
		if err != nil {
			return "", fmt.Errorf("regexp compile error: %v", err)
		}
		match := rexp.FindSubmatch(stat)
		if match == nil {
			return "", fmt.Errorf("no match found using regexp '%s' in /proc/%d/stat", expr, dbp.pid)
		}
		comm = match[1]
	}
	dbp.os.comm = strings.ReplaceAll(string(comm), "%", "%%")

	return getCmdLine(dbp.pid), nil
}

func (dbp *nativeProcess) GetBufferedTracepoints() []ebpf.RawUProbeParams {

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Re-check the target pid: if it exited, attach earlier or use dlv exec on the binary with a held process.
  2. Ensure /proc is mounted and readable for the debugger user (check mount -t proc, hidepid mount options).
  3. Run delve as root or the same user as the target so /proc/<pid>/stat is readable.
  4. Retry attach; the failure is frequently a race with process death.

Example fix

// before
// attach fails: could not read proc stat: open /proc/1234/stat: no such file or directory

// after
if _, err := os.Stat("/proc/1234"); err != nil {
    return fmt.Errorf("target %d already exited before attach; start it under dlv or pause it first", 1234)
}
_ = debugger.Attach(1234, nil)
Defensive patterns

Strategy: validation

Validate before calling

// confirm procfs readable and pid alive before attach on Linux
func pidAttachable(pid int) error {
    if _, err := os.Stat(fmt.Sprintf("/proc/%d/stat", pid)); err != nil {
        return fmt.Errorf("pid %d gone or /proc unreadable: %v", pid, err)
    }
    return nil
}

Try / catch

err := debugger.Attach(pid, nil)
if err != nil && strings.Contains(err.Error(), "could not read proc stat") {
    return fmt.Errorf("%w (target likely exited during attach; retry earlier in process lifetime)", err)
}

Prevention

When it happens

Trigger: initialize (called from Launch/Attach) reads /proc/<pid>/stat and gets an error — the pid no longer exists (process exited during setup), permission denied on /proc, or non-Linux kernel without procfs mounted.

Common situations: Attaching to a short-lived process that dies before initialization completes; running inside containers without /proc mounted or with hidepid=2 on /proc mounts; chroot environments lacking procfs.

Related errors


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