MagicMirrorOrg/MagicMirror · warning
Port ${port} still not available after ${maxAttempts} attemp
Error message
Port ${port} still not available after ${maxAttempts} attempts What it means
serveronly/watcher.js restarts the server process and calls waitForPort to detect when the port is free again. It polls isPortAvailable up to maxAttempts times at PORT_CHECK_INTERVAL_MS; if the port is still occupied after all attempts, this warning is logged, meaning the server restart may fail or the old process is still holding the port.
Source
Thrown at serveronly/watcher.js:83
server.listen(port, address);
});
}
/**
* Wait until port is available
* @param {number} port The port to wait for
* @param {number} maxAttempts Maximum number of attempts
* @returns {Promise<void>}
*/
async function waitForPort (port, maxAttempts = PORT_CHECK_MAX_ATTEMPTS) {
for (let i = 0; i < maxAttempts; i++) {
if (await isPortAvailable(port)) {
Log.info(`Port ${port} is now available`);
return;
}
await new Promise((resolve) => setTimeout(resolve, PORT_CHECK_INTERVAL_MS));
}
Log.warn(`Port ${port} still not available after ${maxAttempts} attempts`);
}
/**
* Start the server process
*/
function startServer () {
// Start node directly instead of via npm to avoid process tree issues
child = spawn("node", ["./serveronly"], {
stdio: "inherit",
cwd: path.join(__dirname, "..")
});
child.on("error", (error) => {
Log.error("Failed to start server process:", error.message);
child = null;
});
child.on("exit", (code, signal) => {View on GitHub (pinned to 4b4a59534f)
Solutions
- Find and kill the process holding the port: `lsof -i :8080` / `ss -ltnp | grep 8080`, then kill it.
- Wait a moment and restart serveronly manually to see if the port frees up.
- Increase PORT_CHECK_INTERVAL_MS / maxAttempts in watcher.js if restarts are regularly slow in your environment.
- If inside Docker, ensure the server receives and honors SIGTERM (e.g. use `node serveronly` with init/tini) so children exit cleanly.
Example fix
// shell $ lsof -t -i :8080 | xargs -r kill $ node serveronly/index.js # restart with port now free
Defensive patterns
Strategy: retry
Validate before calling
// before restarting, confirm the port is free:
const net = require("net");
const s = net.createServer();
s.once("error", () => console.error("Port 8080 still in use"));
s.once("listening", () => { console.log("Port free"); s.close(); });
s.listen(8080); Try / catch
try {
await restartServer();
} catch (err) {
if (/still not available/.test(String(err))) {
await new Promise((r) => setTimeout(r, 2000));
await restartServer(); // retry after the old process exits
} else {
throw err;
}
} Prevention
- Kill stale MagicMirror processes before starting serveronly
- Avoid saving many files in rapid succession when the watcher restarts the server
- Run long-lived servers under a supervisor (systemd/tini) so children exit cleanly
- Increase the watcher's polling attempts/interval for slow environments
When it happens
Trigger: File-change-triggered restart where the previous server process hasn't released the port within the polling window; another process bound to the same port; a hung/stuck server child that never exits.
Common situations: Rapid successive file saves triggering overlapping restarts; a zombie MagicMirror process from a crashed earlier run still listening on 8080; running under Docker where the old child ignores SIGTERM.
AI-assisted analysis of MagicMirrorOrg/MagicMirror@4b4a59534f (2026-08-31).
Data as JSON: /api/errors/097c5dbb8ca4d7d1.
Report an issue: GitHub.