{"record":{"id":"097c5dbb8ca4d7d1","repo":"MagicMirrorOrg/MagicMirror","slug":"port-port-still-not-available-after-maxattemp","errorCode":null,"errorMessage":"Port ${port} still not available after ${maxAttempts} attempts","messagePattern":"Port (.+?) still not available after (.+?) attempts","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"serveronly/watcher.js","lineNumber":83,"sourceCode":"\t\tserver.listen(port, address);\n\t});\n}\n\n/**\n * Wait until port is available\n * @param {number} port The port to wait for\n * @param {number} maxAttempts Maximum number of attempts\n * @returns {Promise<void>}\n */\nasync function waitForPort (port, maxAttempts = PORT_CHECK_MAX_ATTEMPTS) {\n\tfor (let i = 0; i < maxAttempts; i++) {\n\t\tif (await isPortAvailable(port)) {\n\t\t\tLog.info(`Port ${port} is now available`);\n\t\t\treturn;\n\t\t}\n\t\tawait new Promise((resolve) => setTimeout(resolve, PORT_CHECK_INTERVAL_MS));\n\t}\n\tLog.warn(`Port ${port} still not available after ${maxAttempts} attempts`);\n}\n\n/**\n * Start the server process\n */\nfunction startServer () {\n\t// Start node directly instead of via npm to avoid process tree issues\n\tchild = spawn(\"node\", [\"./serveronly\"], {\n\t\tstdio: \"inherit\",\n\t\tcwd: path.join(__dirname, \"..\")\n\t});\n\n\tchild.on(\"error\", (error) => {\n\t\tLog.error(\"Failed to start server process:\", error.message);\n\t\tchild = null;\n\t});\n\n\tchild.on(\"exit\", (code, signal) => {","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/MagicMirrorOrg/MagicMirror/blob/4b4a59534f7da01e4030e46029fe9dd649a7675e/serveronly/watcher.js#L65-L101","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// shell\n$ lsof -t -i :8080 | xargs -r kill\n$ node serveronly/index.js  # restart with port now free","handlingStrategy":"retry","validationCode":"// before restarting, confirm the port is free:\nconst net = require(\"net\");\nconst s = net.createServer();\ns.once(\"error\", () => console.error(\"Port 8080 still in use\"));\ns.once(\"listening\", () => { console.log(\"Port free\"); s.close(); });\ns.listen(8080);","typeGuard":null,"tryCatchPattern":"try {\n  await restartServer();\n} catch (err) {\n  if (/still not available/.test(String(err))) {\n    await new Promise((r) => setTimeout(r, 2000));\n    await restartServer(); // retry after the old process exits\n  } else {\n    throw err;\n  }\n}","preventionTips":["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"],"tags":["port","restart","serveronly","process"],"backgroundTag":"port-already-in-use","analyzedSha":"4b4a59534f7da01e4030e46029fe9dd649a7675e","analyzedAt":"2026-08-31T21:49:42.591Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}