{"record":{"id":"533dae399912820b","repo":"can1357/oh-my-pi","slug":"failed-to-start-stats-dashboard-on-hostname-p","errorCode":null,"errorMessage":"Failed to start stats dashboard on ${hostname}:${port} after reclaiming it.","messagePattern":"Failed to start stats dashboard on (.+?):(.+?) after reclaiming it\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stats/src/server.ts","lineNumber":403,"sourceCode":"\t\t\tstop: () => server.stop(),\n\t\t};\n\t} catch (error) {\n\t\tif (!(error instanceof Error && \"code\" in error && error.code === \"EADDRINUSE\")) throw error;\n\n\t\tconst recovery = await recoverStatsPort(port, hostname);\n\t\tif (recovery === \"reuse\") {\n\t\t\treturn { hostname, port, stop: () => {} };\n\t\t}\n\n\t\ttry {\n\t\t\tconst server = createDashboardServer(port, hostname);\n\t\t\treturn {\n\t\t\t\thostname,\n\t\t\t\tport: server.port ?? port,\n\t\t\t\tstop: () => server.stop(),\n\t\t\t};\n\t\t} catch (retryError) {\n\t\t\tthrow new Error(`Failed to start stats dashboard on ${hostname}:${port} after reclaiming it.`, {\n\t\t\t\tcause: retryError,\n\t\t\t});\n\t\t}\n\t}\n}\n","sourceCodeStart":385,"sourceCodeEnd":409,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/stats/src/server.ts#L385-L409","documentation":"startServer() tries to bind the dashboard's HTTP port; if the port is taken it attempts to reclaim it (e.g. killing the stale listener or retrying the bind). If the bind still fails after that reclaim attempt, it wraps the retry failure (as `cause`) in this error.","triggerScenarios":"Calling startServer({ hostname, port }) where the port is occupied and the reclaim/retry path also fails — e.g. the occupying process can't be killed, permission denied on a privileged port, or an immediate rebind race.","commonSituations":"Another instance of the stats dashboard (or any process) is already listening on the port and refuses to die; running on port <1024 without privileges; hostname unresolvable or in use; container port conflicts.","solutions":["Check what holds the port: `lsof -i :<port>` or `ss -ltnp`, and stop the conflicting process","Pick a different port when calling startServer (or pass port 0 to let the OS choose)","If binding a privileged port, run with adequate privileges or use a high port","Inspect the `cause` of this error for the underlying bind failure"],"exampleFix":"// before\nawait startServer({ port: 3000 })\n// after: avoid hard-coded ports / handle failure\ntry {\n  await startServer({ port: 3000 })\n} catch (err) {\n  await startServer({ port: 0 }) // let OS pick a free port\n}","handlingStrategy":"retry","validationCode":"const inUse = Bun.spawnSync([\"sh\", \"-c\", `ss -ltn | grep ':${port} '`]).exitCode === 0;\nif (inUse) port = 0; // let OS pick a free port","typeGuard":null,"tryCatchPattern":"try {\n  return await startServer({ hostname, port });\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"after reclaiming it\")) {\n    return await startServer({ hostname, port: 0 }); // fallback to ephemeral port\n  }\n  throw err;\n}","preventionTips":["Don't hard-code ports; prefer 0 (ephemeral) in shared/dev environments","Check for stale dashboard instances before launching","Avoid privileged ports (<1024) unless running with the right privileges"],"tags":["network","port-conflict","server"],"backgroundTag":"port-already-in-use","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}