{"record":{"id":"6c65ac71ed32302f","repo":"n8n-io/n8n","slug":"port-config-port-is-already-in-use","errorCode":null,"errorMessage":"Port {config.port} is already in use","messagePattern":"Port (.+?) is already in use","errorType":"exception","errorClass":"OSError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/task-runner-python/src/health_check_server.py","lineNumber":29,"sourceCode":"\nclass HealthCheckServer:\n    def __init__(self):\n        self.server: asyncio.Server | None = None\n        self.logger = logging.getLogger(__name__)\n\n    async def start(self, config: HealthCheckConfig) -> None:\n        try:\n            self.server = await asyncio.start_server(\n                self._handle_request, config.host, config.port\n            )\n            # for OS-assigned port in tests\n            actual_port = self.server.sockets[0].getsockname()[1]\n            self.logger.info(\n                f\"Health check server listening on {config.host}, port {actual_port}\"\n            )\n        except OSError as e:\n            if e.errno == errno.EADDRINUSE:\n                raise OSError(f\"Port {config.port} is already in use\") from e\n            else:\n                raise\n\n    async def stop(self) -> None:\n        if self.server:\n            self.server.close()\n            await self.server.wait_closed()\n            self.server = None\n            self.logger.info(\"Health check server stopped\")\n\n    async def _handle_request(\n        self, _reader: asyncio.StreamReader, writer: asyncio.StreamWriter\n    ) -> None:\n        try:\n            writer.write(HEALTH_CHECK_RESPONSE)\n            await writer.drain()\n        except Exception:\n            pass","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/task-runner-python/src/health_check_server.py#L11-L47","documentation":"Thrown by HealthCheckServer.start when asyncio.start_server fails with EADDRINUSE, meaning the configured health check port is already bound by another process. The original OSError is chained (from e) so the traceback is preserved, but the message is made more user-friendly by naming the specific port.","triggerScenarios":"The Python task runner attempts to bind its health check TCP server to config.port, but another process (another runner instance, a different service, or a stale runner process) is already listening on that port. asyncio.start_server raises OSError with errno EADDRINUSE.","commonSituations":"Running multiple runner instances on the same host with the same health check port. A previous runner process didn't shut down cleanly and is still holding the port. Another application happens to use the same port. Port conflict in a container orchestration environment where multiple pods share host networking.","solutions":["Find and stop the process holding the port: use 'lsof -i :<port>' or 'ss -tlnp | grep <port>'.","Change N8N_RUNNERS_HEALTH_CHECK_SERVER_PORT to a different available port.","Set the port to 0 to let the OS assign an available port automatically (useful in tests).","Ensure previous runner processes are fully terminated before starting new ones."],"exampleFix":"# before\nexport N8N_RUNNERS_HEALTH_CHECK_SERVER_PORT=8080\n# another process is on 8080\n# after — use a different port\nexport N8N_RUNNERS_HEALTH_CHECK_SERVER_PORT=8081\n\n# or find and kill the stale process\n# lsof -i :8080  # then kill <PID>","handlingStrategy":"try-catch","validationCode":"import socket\n\ndef is_port_available(host: str, port: int) -> bool:\n    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:\n        try:\n            s.bind((host, port))\n            return True\n        except OSError:\n            return False\n\nif not is_port_available(config.host, config.port):\n    raise RuntimeError(f'Port {config.port} is already in use')","typeGuard":null,"tryCatchPattern":"from health_check_server import HealthCheckServer\n\ntry:\n    await server.start(config)\nexcept OSError as e:\n    if 'already in use' in str(e):\n        print(f'Port {config.port} is busy — try a different port or stop the conflicting process')\n        config.port = 0  # let OS assign\n        await server.start(config)","preventionTips":["Use port 0 for OS-assigned ports in development and testing.","Document the health check port and ensure it doesn't conflict with other services.","Ensure previous runner processes are terminated before starting new ones.","Use liveness probes that don't conflict with the health check server."],"tags":["task-runner","python","network","port-conflict","health-check","startup"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}