getzola/zola · error

Could not bind to address

Error message

Could not bind to address

What it means

Inside the spawned server thread, serve() awaits tokio::net::TcpListener::bind(&bind_address) and expects success. The expect fires when the configured address/port cannot be bound — most commonly the port is already in use by another process, or the user lacks permission for the port (privileged ports <1024) or the interface does not exist. Earlier in serve() a synchronous TcpListener::bind probe already ran; this is the actual async bind the server depends on, so failure means the dev server cannot listen.

Source

Thrown at src/cmd/serve.rs:651

    thread::spawn(move || {
        let rt = tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()
            .expect("Could not build tokio runtime");

        rt.block_on(async {
            let state = Arc::new(AppState { static_root, base_path, reload_tx });

            let app = Router::new()
                .route("/livereload.js", get(serve_livereload_js))
                .route("/livereload", get(ws_handler))
                .fallback(handle_request)
                .layer(middleware::map_response(error_injection_middleware))
                .with_state(state);

            let listener = tokio::net::TcpListener::bind(&bind_address)
                .await
                .expect("Could not bind to address");

            let local_addr = listener.local_addr().unwrap();

            log::info!(
                "Web server is available at {} (bound to {})\n",
                constructed_base_url.replace(&bind_address.to_string(), &local_addr.to_string()),
                local_addr
            );
            if open && let Err(err) = open::that(&constructed_base_url) {
                log::error!("Failed to open URL in your browser: {err}");
            }

            axum::serve(listener, app).await.expect("Could not start web server");
        });
    });

    // We watch for changes in the config by monitoring its parent directory, but we ignore all
    // ordinary peer files. Map the parent directory back to the config file name to not confuse

View on GitHub (pinned to 61d3082821)

Solutions

  1. Free the port: stop the other process listening on bind_address (lsof/netstat can identify it)
  2. Choose a different port via the --port flag or let the OS pick a free port
  3. Avoid privileged ports below 1024 or run with sufficient permissions
  4. Replace expect() with an anyhow error that includes bind_address and the underlying io::Error
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at src/cmd/serve.rs:651 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of getzola/zola@61d3082821 (2026-09-03). Data as JSON: /api/errors/e206c55171f99df2. Report an issue: GitHub.