{"record":{"id":"c2027f681ed45756","repo":"XTLS/Xray-core","slug":"could-not-get-process-name-for-pid","errorCode":null,"errorMessage":"could not get process name for PID : ","messagePattern":"could not get process name for PID : ","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"common/net/find_process_linux.go","lineNumber":71,"sourceCode":"\tinode, err := findInodeInFile(procFile, targetHexAddr)\n\tif err != nil {\n\t\treturn 0, \"\", \"\", errors.New(\"could not search in \", procFile).Base(err)\n\t}\n\tif inode == \"\" {\n\t\treturn 0, \"\", \"\", errors.New(\"connection for \", srcIP, \":\", srcPort, \" not found in \", procFile)\n\t}\n\n\tpidStr, err := findPidByInode(inode)\n\tif err != nil {\n\t\treturn 0, \"\", \"\", errors.New(\"could not find PID for inode \", inode, \": \", err)\n\t}\n\tif pidStr == \"\" {\n\t\treturn 0, \"\", \"\", errors.New(\"no process found for inode \", inode)\n\t}\n\n\tabsPath, err := getAbsPath(pidStr)\n\tif err != nil {\n\t\treturn 0, \"\", \"\", errors.New(\"could not get process name for PID \", pidStr, \":\", err)\n\t}\n\n\tnameSplit := strings.Split(absPath, \"/\")\n\tprocName := nameSplit[len(nameSplit)-1]\n\n\tpid, err := strconv.Atoi(pidStr)\n\tif err != nil {\n\t\treturn 0, \"\", \"\", errors.New(\"failed to parse PID: \", err)\n\t}\n\n\treturn pid, procName, absPath, nil\n}\n\nfunc formatLittleEndianString(addr net.IP, port Port) (string, error) {\n\tip := addr\n\tvar ipBytes []byte\n\tif ip.To4() != nil {\n\t\tipBytes = ip.To4()","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/common/net/find_process_linux.go#L53-L89","documentation":"A PID string was found for the socket inode, but reading its executable path (getAbsPath -> readlink /proc/<pid>/exe) failed; the underlying readlink error is appended. This is nearly always a race: the process exited between the FD scan and the exe readlink, or the caller lacks permission to read another user's /proc/<pid>/exe.","triggerScenarios":"Matched process exits immediately after accepting/closing the connection; unprivileged caller matching a root process (readlink of /proc/<pid>/exe gives EACCES for foreign processes); zombie processes (exe link already gone).","commonSituations":"Short-lived connection handlers (per-request curl, forked workers); multi-user systems with process routing enabled.","solutions":["Check the appended error: ESRCH/ENOENT = process exited (benign race), EACCES = permission","Retry the whole lookup once if the connection is still alive","Run elevated when cross-user path resolution is needed","Fall back to routing without the process name/path"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isProcessNameError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"could not get process name for PID\")\n}","tryCatchPattern":"pid, name, path, err := net.FindProcess(netw, srcIP, srcPort, dstIP, dstPort)\nif err != nil && isProcessNameError(err) {\n    // /proc/<pid>/exe readlink raced with exit; one retry is cheap\n    pid, name, path, err = net.FindProcess(netw, srcIP, srcPort, dstIP, dstPort)\n}","preventionTips":["Budget exactly one retry for exit races","Check for EACCES in the appended error to detect privilege issues instead of retrying them"],"tags":["linux","process-lookup","race-condition","permissions","procfs"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}