{"record":{"id":"e16332016722f392","repo":"pranshuparmar/witr","slug":"listfds-returned-partial-record","errorCode":null,"errorMessage":"listfds returned partial record","messagePattern":"listfds returned partial record","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/proc/libproc_darwin_cgo.go","lineNumber":177,"sourceCode":"\n\tfor {\n\t\tvar used C.int\n\t\terrno := C.witr_proc_pidlistfds(C.int(pid), &entries[0], C.int(len(entries)*bytesPerEntry), &used)\n\t\tif errno == C.EINVAL && len(entries) < 16384 {\n\t\t\tentries = make([]C.struct_proc_fdinfo, len(entries)*2)\n\t\t\tcontinue\n\t\t}\n\t\tif errno != 0 {\n\t\t\tswitch errno {\n\t\t\tcase C.ESRCH, C.EPERM:\n\t\t\t\treturn 0, nil, nil\n\t\t\tdefault:\n\t\t\t\treturn 0, nil, fmt.Errorf(\"proc_pidinfo listfds: %d\", errno)\n\t\t\t}\n\t\t}\n\t\tbytesUsed := int(used)\n\t\tif bytesUsed%bytesPerEntry != 0 {\n\t\t\treturn 0, nil, errors.New(\"listfds returned partial record\")\n\t\t}\n\t\tcount := bytesUsed / bytesPerEntry\n\t\treturn count, formatFDEntries(pid, entries[:count]), nil\n\t}\n}\n\nfunc formatFDEntries(pid int, entries []C.struct_proc_fdinfo) []string {\n\tvar out []string\n\tfor _, entry := range entries {\n\t\tif len(out) >= 10 {\n\t\t\tbreak\n\t\t}\n\t\tfd := int(entry.proc_fd)\n\t\tlabel := fdTypeLabel(entry.proc_fdtype)\n\t\tswitch entry.proc_fdtype {\n\t\tcase C.PROX_FDTYPE_VNODE:\n\t\t\tvar vnode C.struct_vnode_fdinfowithpath\n\t\t\tif errno := C.witr_proc_pidfdinfo_vnode(C.int(pid), C.int(fd), &vnode); errno == 0 {","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/pranshuparmar/witr/blob/dc4fa1da82d3e266fcbd928641b4f30b3077c64f/internal/proc/libproc_darwin_cgo.go#L159-L195","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nfds, err := readDarwinFDs(pid)\nif err != nil { return err }\n// after\nvar fds []FDInfo\nvar err error\nfor i := 0; i < 3; i++ {\n    fds, err = readDarwinFDs(pid)\n    if err == nil || !strings.Contains(err.Error(), \"partial record\") { break }\n    time.Sleep(50 * time.Millisecond) // fd table churn; retry the snapshot\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"var fds []FDInfo\nvar lastErr error\nfor i := 0; i < 3; i++ {\n    fds, lastErr = readDarwinFDs(pid)\n    if lastErr == nil || !strings.Contains(lastErr.Error(), \"partial record\") {\n        break\n    }\n    time.Sleep(50 * time.Millisecond)\n}\nif lastErr != nil {\n    return fmt.Errorf(\"fd snapshot for pid %d failed after retries: %w\", pid, lastErr)\n}","preventionTips":["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."],"tags":["macos","process-inspection","race-condition","cgo"],"backgroundTag":"fd-list-partial-record","analyzedSha":"dc4fa1da82d3e266fcbd928641b4f30b3077c64f","analyzedAt":"2026-09-01T12:17:08.767Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}