can1357/oh-my-pi · error · Error
Failed to start stats dashboard on ${hostname}:${port} after
Error message
Failed to start stats dashboard on ${hostname}:${port} after reclaiming it. What it means
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.
Source
Thrown at packages/stats/src/server.ts:403
stop: () => server.stop(),
};
} catch (error) {
if (!(error instanceof Error && "code" in error && error.code === "EADDRINUSE")) throw error;
const recovery = await recoverStatsPort(port, hostname);
if (recovery === "reuse") {
return { hostname, port, stop: () => {} };
}
try {
const server = createDashboardServer(port, hostname);
return {
hostname,
port: server.port ?? port,
stop: () => server.stop(),
};
} catch (retryError) {
throw new Error(`Failed to start stats dashboard on ${hostname}:${port} after reclaiming it.`, {
cause: retryError,
});
}
}
}
View on GitHub (pinned to 9690622007)
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
Example fix
// before
await startServer({ port: 3000 })
// after: avoid hard-coded ports / handle failure
try {
await startServer({ port: 3000 })
} catch (err) {
await startServer({ port: 0 }) // let OS pick a free port
} Defensive patterns
Strategy: retry
Validate before calling
const inUse = Bun.spawnSync(["sh", "-c", `ss -ltn | grep ':${port} '`]).exitCode === 0;
if (inUse) port = 0; // let OS pick a free port Try / catch
try {
return await startServer({ hostname, port });
} catch (err) {
if (err instanceof Error && err.message.includes("after reclaiming it")) {
return await startServer({ hostname, port: 0 }); // fallback to ephemeral port
}
throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Port ${host}:${chosen} is already in use; cannot start remot
- timed out: {command}
- V2 remote compaction failed (${response.status} ${response.s
- V2 compaction stream closed before response.completed
- Auth broker response failed schema validation
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/533dae399912820b.
Report an issue: GitHub.