pranshuparmar/witr · error
listfds returned partial record
Error message
listfds returned partial record
What it means
readDarwinFDs calls proc_pidinfo with the PROC_PIDLISTFDS selector to enumerate a process's file descriptors. The kernel returned a byte count that is not an exact multiple of the per-entry struct size (proc_fdinfo), meaning the fd list changed while it was being read or the buffer interpretation is inconsistent. Rather than returning a truncated/corrupt entry array, the library fails fast with this error.
Source
Thrown at internal/proc/libproc_darwin_cgo.go:177
for {
var used C.int
errno := C.witr_proc_pidlistfds(C.int(pid), &entries[0], C.int(len(entries)*bytesPerEntry), &used)
if errno == C.EINVAL && len(entries) < 16384 {
entries = make([]C.struct_proc_fdinfo, len(entries)*2)
continue
}
if errno != 0 {
switch errno {
case C.ESRCH, C.EPERM:
return 0, nil, nil
default:
return 0, nil, fmt.Errorf("proc_pidinfo listfds: %d", errno)
}
}
bytesUsed := int(used)
if bytesUsed%bytesPerEntry != 0 {
return 0, nil, errors.New("listfds returned partial record")
}
count := bytesUsed / bytesPerEntry
return count, formatFDEntries(pid, entries[:count]), nil
}
}
func formatFDEntries(pid int, entries []C.struct_proc_fdinfo) []string {
var out []string
for _, entry := range entries {
if len(out) >= 10 {
break
}
fd := int(entry.proc_fd)
label := fdTypeLabel(entry.proc_fdtype)
switch entry.proc_fdtype {
case C.PROX_FDTYPE_VNODE:
var vnode C.struct_vnode_fdinfowithpath
if errno := C.witr_proc_pidfdinfo_vnode(C.int(pid), C.int(fd), &vnode); errno == 0 {View on GitHub (pinned to dc4fa1da82)
Solutions
- Retry the fd snapshot; a transient race is the usual cause and a second read typically returns a consistent record set.
- Freeze or quiesce the target (SIGSTOP, or run against a process with stable fd usage) before enumerating.
- Retry a few times with a small backoff in your monitoring code before surfacing the failure.
- If it reproduces on a quiesced process, verify macOS/Xcode SDK version matches what the library's proc_fdinfo sizing assumes and file a bug.
Example fix
// before
fds, err := readDarwinFDs(pid)
if err != nil { return err }
// after
var fds []FDInfo
var err error
for i := 0; i < 3; i++ {
fds, err = readDarwinFDs(pid)
if err == nil || !strings.Contains(err.Error(), "partial record") { break }
time.Sleep(50 * time.Millisecond) // fd table churn; retry the snapshot
} Defensive patterns
Strategy: retry
Try / catch
var fds []FDInfo
var lastErr error
for i := 0; i < 3; i++ {
fds, lastErr = readDarwinFDs(pid)
if lastErr == nil || !strings.Contains(lastErr.Error(), "partial record") {
break
}
time.Sleep(50 * time.Millisecond)
}
if lastErr != nil {
return fmt.Errorf("fd snapshot for pid %d failed after retries: %w", pid, lastErr)
} Prevention
- Prefer quiescent processes for fd snapshots; SIGSTOP high-churn servers before enumerating.
- Always retry transient snapshot errors rather than failing the whole inspection.
- Pin macOS SDK versions in CI so proc_fdinfo sizing assumptions hold.
When it happens
Trigger: Calling readDarwinFDs (the fds / open-files inspection on macOS) when the target process opens or closes file descriptors concurrently with the proc_pidinfo(PROC_PIDLISTFDS) call, so the reported bytesUsed is not divisible by bytesPerEntry.
Common situations: Inspecting a high-churn process (a server constantly accepting/closing sockets, a logging daemon rotating files); inspecting a short-lived process that exits during enumeration; racing snapshots in a monitoring loop.
Related errors
- launchctl blame failed: %w
- no service label found for pid %d
- process not managed by a named launchd service: %s
- no process ancestry found
- failed to convert plist: %w
AI-assisted analysis of pranshuparmar/witr@dc4fa1da82 (2026-09-01).
Data as JSON: /api/errors/e16332016722f392.
Report an issue: GitHub.