{"record":{"id":"f8c431c89acd4878","repo":"tauri-apps/tauri","slug":"couldn-t-bind-to-port-on-ip","errorCode":null,"errorMessage":"Couldn't bind to {port} on {ip}","messagePattern":"Couldn't bind to (.+?) on (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tauri-cli/src/dev/builtin_dev_server.rs","lineNumber":46,"sourceCode":"}\n\npub fn start<P: AsRef<Path>>(dir: P, ip: IpAddr, port: Option<u16>) -> crate::Result<SocketAddr> {\n  let dir = dir.as_ref();\n  let dir =\n    dunce::canonicalize(dir).fs_context(\"failed to canonicalize path\", dir.to_path_buf())?;\n\n  // bind port and tcp listener\n  let auto_port = port.is_none();\n  let mut port = port.unwrap_or(1430);\n  let (tcp_listener, address) = loop {\n    let address = SocketAddr::new(ip, port);\n    if let Ok(tcp) = std::net::TcpListener::bind(address) {\n      tcp.set_nonblocking(true).unwrap();\n      break (tcp, address);\n    }\n\n    if !auto_port {\n      crate::error::bail!(\"Couldn't bind to {port} on {ip}\");\n    }\n\n    port += 1;\n  };\n\n  let (tx, _) = channel(1);\n\n  // watch dir for changes\n  let tx_c = tx.clone();\n  watch(dir.clone(), move || {\n    let _ = tx_c.send(());\n  });\n\n  let state = ServerState { dir, tx, address };\n\n  // start router thread\n  std::thread::spawn(move || {\n    tokio::runtime::Builder::new_current_thread()","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/tauri-apps/tauri/blob/52e4b6e71d8632a7e648f866c442e287ecddee34/crates/tauri-cli/src/dev/builtin_dev_server.rs#L28-L64","documentation":"The built-in dev server (`tauri dev` without a `devUrl`) binds a TCP listener on the given IP. When a port was explicitly requested (`auto_port == false`) and `TcpListener::bind` fails — port already in use, or a privileged/permission-denied port — it aborts. With no explicit port it silently increments from 1430 until a free port is found.","triggerScenarios":"Starting the built-in dev server with an explicit `--port 1430` while another process (previous dev run, another app, a crashed orphan process) holds that port, or binding a port below 1024 without privileges.","commonSituations":"Two `tauri dev` instances with a fixed port; a previous dev process that did not exit cleanly; Docker/other services occupying the configured port in CI.","solutions":["Free the port: find the holder with `lsof -i :1430` (macOS/Linux) or `netstat -ano | findstr 1430` (Windows) and kill it","Run with a different port: `tauri dev --port 1431`","Omit the explicit port so the CLI auto-picks the first free port starting at 1430"],"exampleFix":"# before\ntauri dev --port 1430   # port already in use\n\n# after\nkill $(lsof -t -i:1430) 2>/dev/null; tauri dev --port 1430\n# or simply: tauri dev   # auto-selects a free port","handlingStrategy":"validation","validationCode":"# check the port is free before starting the built-in dev server\nPORT=1430\nif command -v lsof >/dev/null; then lsof -i :\"$PORT\" >/dev/null 2>&1 && { echo \"port $PORT busy\" >&2; exit 1; }\nelse netstat -ltn 2>/dev/null | grep -q \":$PORT \" && { echo \"port $PORT busy\" >&2; exit 1; }\nfi\ntauri dev --port $PORT","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Omit `--port` to let the CLI auto-increment from 1430","Kill orphaned dev-server processes before starting a new session","In CI, allocate dynamic ports or check availability first"],"tags":["tauri-cli","dev-server","network","port","bind"],"backgroundTag":"port-already-in-use","analyzedSha":"52e4b6e71d8632a7e648f866c442e287ecddee34","analyzedAt":"2026-08-20T13:59:20.734Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}