{"record":{"id":"93ff5bdea7d69705","repo":"can1357/oh-my-pi","slug":"port-port-is-in-use-but-the-listening-process","errorCode":null,"errorMessage":"Port ${port} is in use, but the listening process could not be identified.","messagePattern":"Port (.+?) is in use, but the listening process could not be identified\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stats/src/port-conflict.ts","lineNumber":224,"sourceCode":"\t\t} catch (error) {\n\t\t\tif (error instanceof Error && \"code\" in error && error.code === \"ESRCH\") return;\n\t\t\tthrow new Error(`Failed to inspect ${holder.image} (PID ${holder.pid})`, { cause: error });\n\t\t}\n\t}\n\n\ttry {\n\t\tprocess.kill(holder.pid, \"SIGKILL\");\n\t} catch (error) {\n\t\tif (error instanceof Error && \"code\" in error && error.code === \"ESRCH\") return;\n\t\tthrow new Error(`Failed to kill ${holder.image} (PID ${holder.pid})`, { cause: error });\n\t}\n\tawait Bun.sleep(PROCESS_EXIT_POLL_MS);\n}\n\nasync function reclaimStatsPort(port: number): Promise<\"retry\"> {\n\tconst holder = await findPortHolder(port);\n\tif (!holder) {\n\t\tthrow new Error(`Port ${port} is in use, but the listening process could not be identified.`);\n\t}\n\tif (holder.pid === process.pid) {\n\t\tthrow new Error(`Port ${port} is held by the current process (${holder.image}, PID ${holder.pid}).`);\n\t}\n\n\tconst normalizedImage = holder.image\n\t\t.toLowerCase()\n\t\t.replace(/\\.exe$/, \"\")\n\t\t.replace(/ \\(deleted\\)$/, \"\");\n\tconst normalizedCommand = holder.commandLine.toLowerCase().replaceAll(\"\\\\\", \"/\");\n\tconst hasStatsIdentity =\n\t\tnormalizedImage === \"omp-stats\" ||\n\t\t/(?:^|[/\"'\\s])omp-stats(?:\\.exe)?(?:[\"'\\s]|$)/.test(normalizedCommand) ||\n\t\t/\\/packages\\/stats\\/src\\/index\\.ts(?:[\"'\\s]|$)/.test(normalizedCommand) ||\n\t\t(normalizedImage === \"omp\" && /(?:^|\\s)stats(?:\\s|$)/.test(normalizedCommand)) ||\n\t\t/(?:^|\\/)omp(?:\\.exe)?[\"'\\s]+stats(?:[\"'\\s]|$)/.test(normalizedCommand);\n\tif (!STATS_RUNTIME_IMAGES[normalizedImage] || !hasStatsIdentity) {\n\t\tthrow new Error(","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/stats/src/port-conflict.ts#L206-L242","documentation":"reclaimStatsPort() calls findPortHolder() to map the port to a owning process. When the port is reported in use but findPortHolder() returns null — no owning process could be identified from the OS tooling — this error is thrown rather than killing an unknown target. It is a deliberate safety stop: without a PID/image the code will not attempt termination.","triggerScenarios":"Port is occupied (bind/listen fails) but findPortHolder() cannot resolve an owner — e.g. lsof/ss output unavailable or empty for that port, a foreign-namespace socket, or a race where the holder exited between the bind attempt and the lookup.","commonSituations":"Minimal containers or Windows environments lacking lsof/netstat equivalents the lookup relies on; a socket held by a kernel service; the port just released but still in TIME_WAIT while a probe still reports it busy.","solutions":["Install or expose the process-listing tooling the environment needs (lsof/ss) so the holder can be identified","Wait a moment and retry — the holder may have exited between checks","Free the port manually (netstat -ano / lsof -i :<port>, then kill the PID) and restart the stats server","Configure the stats server to use a different port"],"exampleFix":"// before\nawait prepareStatsPort(port); // throws when holder unidentifiable\n// after\ntry {\n  await prepareStatsPort(port);\n} catch (err) {\n  if (err.message.includes('could not be identified')) {\n    await Bun.sleep(1000);\n    await prepareStatsPort(port); // holder may have exited; or pick another port\n  } else throw err;\n}","handlingStrategy":"retry","validationCode":"const busy = await isPortInUse(port);\nconst holder = busy ? await findPortHolder(port) : null;\nif (busy && !holder) console.warn(`Port ${port} busy but owner unidentified — resolve manually before starting stats`);","typeGuard":null,"tryCatchPattern":"try {\n  await prepareStatsPort(port);\n} catch (err) {\n  if (err.message.includes('could not be identified')) {\n    await Bun.sleep(1500);\n    await prepareStatsPort(port); // holder may have exited; else pick another port\n  } else throw err;\n}","preventionTips":["Install lsof/ss (or Windows equivalents) so port owners can be identified","Retry briefly on startup — transient TIME_WAIT races resolve themselves","Fall back to an alternate port instead of blocking startup","Free unidentifiable sockets manually via netstat/lsof before launching"],"tags":["port","process-identification","network"],"backgroundTag":"port-holder-unidentified","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}