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

  1. Find and kill the process holding the port: `lsof -i :8080` / `ss -ltnp | grep 8080`, then kill it.
  2. Wait a moment and restart serveronly manually to see if the port frees up.
  3. Increase PORT_CHECK_INTERVAL_MS / maxAttempts in watcher.js if restarts are regularly slow in your environment.
  4. 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

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.