{"record":{"id":"f72dc2c06e3a9faa","repo":"zeroclaw-labs/zeroclaw","slug":"port-port-is-already-in-use-so-the-gateway-coul","errorCode":null,"errorMessage":"Port {port} is already in use, so the gateway could not start.","messagePattern":"Port (.+?) is already in use, so the gateway could not start\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/main.rs","lineNumber":8132,"sourceCode":"    // can self-respawn after the listener is released. Must mirror the same\n    // call in the Daemon branch.\n    zeroclaw_runtime::restart::record_launch();\n    // Standalone gateway (no daemon supervisor): pass None for reload_tx so\n    // /admin/reload returns 503 with a clear \"no supervisor; restart\n    // manually\" message, None for tui_registry (no TUI socket), and None\n    // for canvas_store so the gateway falls back to its own default.\n    let result = Box::pin(gateway::run_gateway(\n        host, port, config, tx, None, None, None, None, None, None,\n    ))\n    .await;\n    // Self-respawn after the listener is released, if an in-app upgrade\n    // requested it. No-op when no respawn was requested or on supervised\n    // restart modes.\n    zeroclaw_runtime::restart::respawn_if_requested();\n    match result {\n        Err(err) if is_addr_in_use_error(&err) => {\n            let restart_port = available_gateway_restart_hint_port(host, port);\n            anyhow::bail!(\n                \"{}\",\n                gateway_addr_in_use_message(host, port, &default_host, default_port, restart_port)\n            );\n        }\n        other => other,\n    }\n}\n\n#[cfg(not(feature = \"gateway\"))]\n#[allow(clippy::unused_async)]\nasync fn run_gateway_if_enabled(\n    _host: &str,\n    _port: u16,\n    _config: zeroclaw::config::Config,\n    _tx: Option<tokio::sync::broadcast::Sender<serde_json::Value>>,\n) -> anyhow::Result<()> {\n    anyhow::bail!(\"Gateway feature is not enabled. Rebuild with --features gateway\")\n}","sourceCodeStart":8114,"sourceCodeEnd":8150,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/src/main.rs#L8114-L8150","documentation":"The gateway HTTP listener could not bind because the requested address (host:port) is already taken by another process. The code detects this by walking the error chain with is_addr_in_use_error (downcasting to std::io::Error with kind AddrInUse) and replaces the raw io error with an actionable message that includes a restart hint port from available_gateway_restart_hint_port.","triggerScenarios":"Starting zeroclaw while another zeroclaw instance, a respawned child, or any unrelated process (dev server, proxy) already listens on the configured gateway host:port. Also happens when a supervised restart races the old process shutting down.","commonSituations":"Two gateway instances configured with the same port; a previous instance that did not fully exit (orphan/respawn race); another service (nginx, node dev server) occupying the port; docker port mappings colliding.","solutions":["Free the port: stop the other process (`lsof -i :<port>` / `ss -ltnp` to find it)","Use the restart hint port the error message suggests, or change gateway port in the config and restart","If it is a respawn race after a restart, wait a moment and start again — the old listener releases the socket","Set a distinct port per instance when running multiple gateways"],"exampleFix":"# before\nport = 8080   # already bound by another process\n\n# after\nport = 8081   # or kill the holder: kill $(lsof -t -i :8080)","handlingStrategy":"validation","validationCode":"// Probe the bind before handing the port to the gateway:\nuse tokio::net::TcpListener;\nmatch TcpListener::bind((host, port)).await {\n    Ok(_) => { /* port free; drop the probe listener immediately */ }\n    Err(e) if e.kind() == std::io::ErrorKind::AddrInUse => {\n        // pick another port or surface a clear message before starting\n    }\n    Err(e) => return Err(e.into()),\n}","typeGuard":"// Reuse the same chain-walking check the source uses:\nfn is_addr_in_use_error(err: &anyhow::Error) -> bool {\n    err.chain().any(|c| c.downcast_ref::<std::io::Error>()\n        .map_or(false, |io| io.kind() == std::io::ErrorKind::AddrInUse))\n}","tryCatchPattern":"// Catch on the serve call, branch on AddrInUse via the chain (as main.rs does),\n// then either retry with the suggested hint port or fail with the actionable message.","preventionTips":["Reserve one port per gateway instance in config management","Set SO_REUSEADDR-equivalent behavior via quick shutdown handling on restart","Add a boot-time port probe in orchestration scripts","On supervised restarts, wait for the old process to release the socket before respawning"],"tags":["network","bind","port","gateway","startup"],"backgroundTag":"address-in-use","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}