prometheus/node_exporter · error
unable to list all threads for pid
Error message
unable to list all threads for pid: %d %w
What it means
After opening the task directory, getThreadStates calls fs.AllProcs() on it to list the pid's threads; a non-ignored error is wrapped as 'unable to list all threads for pid: %d'. Note the message omits a colon before %w. This aborts the thread-state accounting for the whole scrape.
Solutions
- Read the wrapped cause: for EACCES/EPERM, grant the exporter user access (root, hidepid gid option, or LSM allow rule).
- Verify the task directory is listable: ls /proc/<pid>/task as the exporter user.
- If the process is exiting during scrape, upgrade node_exporter/procfs — newer code treats vanished entries as ignorable.
- Retry the scrape; persistent failures indicate a systematic permission problem, not a race.
Defensive patterns
Strategy: retry
Validate before calling
// check task dir listing works for the exporter user
if _, err := os.ReadDir("/proc/self/task"); err != nil {
log.Printf("task listing broken: %v", err)
} Try / catch
if err := c.Update(ch); err != nil {
if strings.Contains(err.Error(), "unable to list all threads") {
log.Printf("thread enumeration failed for a pid; check task dir permissions: %v", err)
// retry on next scrape
}
} Prevention
- Ensure readdir permission on /proc/<pid>/task for the exporter user.
- Avoid procfs masking by container runtimes for the task hierarchy.
- Upgrade node_exporter/procfs to treat vanishing threads as ignorable errors.
- Treat single-scrape failures as transient; alert on persistence.
When it happens
Trigger: AllProcs() on the /proc/<pid>/task filesystem returns an error not classified as ignorable — readdir of the task directory failed with EACCES/EPERM or another non-ENOENT error.
Common situations: hidepid or LSM restrictions preventing task directory listing for other users' processes; procfs mount corruption; unusual container runtimes exposing a partial procfs.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- error reading task for pid
- error reading stat for pid
- unable to retrieve limit number of threads
- unable to list all processes
- error reading stat for pid
AI-assisted analysis of prometheus/node_exporter@17ddd77c59 (2026-09-07).
Data as JSON: /api/errors/c42060662e8438ca.
Report an issue: GitHub.
Appendix: source
Thrown at collector/processes_linux.go:165
func (c *processCollector) getThreadStates(pid int, pidStat procfs.ProcStat, threadStates map[string]int32) error {
fs, err := procfs.NewFS(procFilePath(path.Join(strconv.Itoa(pid), "task")))
if err != nil {
if c.isIgnoredError(err) {
c.logger.Debug("file not found when retrieving tasks for pid", "pid", pid, "err", err)
return nil
}
c.logger.Debug("error reading tasks for pid", "pid", pid, "err", err)
return fmt.Errorf("error reading task for pid %d: %w", pid, err)
}
t, err := fs.AllProcs()
if err != nil {
if c.isIgnoredError(err) {
c.logger.Debug("file not found when retrieving tasks for pid", "pid", pid, "err", err)
return nil
}
return fmt.Errorf("unable to list all threads for pid: %d %w", pid, err)
}
for _, thread := range t {
if pid == thread.PID {
threadStates[pidStat.State]++
continue
}
threadStat, err := thread.Stat()
if err != nil {
if c.isIgnoredError(err) {
c.logger.Debug("file not found when retrieving stats for thread", "pid", pid, "threadId", thread.PID, "err", err)
continue
}
c.logger.Debug("error reading stat for thread", "pid", pid, "threadId", thread.PID, "err", err)
return fmt.Errorf("error reading stat for pid:%d thread:%d err:%w", pid, thread.PID, err)
}
threadStates[threadStat.State]++
}View on GitHub (pinned to 17ddd77c59)