xai-org/grok-build · error

bind 127.0.0.1:{port}: {e}

Error message

bind 127.0.0.1:{port}: {e}

What it means

serve() failed to bind the diagnostics TCP listener on 127.0.0.1:{port}. Tokio's TcpListener::bind returned an OS error (address in use, permission denied, etc.) which is wrapped into this anyhow error. The diagnostics HTTP server cannot start, so all readiness/statusz checks against it fail.

Source

Thrown at crates/codegen/xai-grok-diag-server/src/lib.rs:435

                    error = %e,
                    "failed to restrict diagnostics socket permissions"
                );
            }
            let task = tokio::spawn(async move {
                if let Err(e) = axum::serve(listener, router(ctx)).await {
                    tracing::warn!(error = %e, "diagnostics server exited");
                }
            });
            Ok(BoundDiag {
                addr: format!("unix:{}", path.display()),
                port: None,
                task,
            })
        }
        DiagListener::Tcp(port) => {
            let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, port))
                .await
                .map_err(|e| anyhow!("bind 127.0.0.1:{port}: {e}"))?;
            let local = listener.local_addr()?;
            let task = tokio::spawn(async move {
                if let Err(e) = axum::serve(listener, router(ctx)).await {
                    tracing::warn!(error = %e, "diagnostics server exited");
                }
            });
            Ok(BoundDiag {
                addr: format!("http://{local}"),
                port: Some(local.port()),
                task,
            })
        }
    }
}

#[derive(Debug)]
pub struct BoundDiag {
    /// Human-readable bound address for the startup log line.

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Find and stop the process occupying the port (lsof -i :PORT / ss -ltnp), or kill the stale diag server
  2. Choose a different, free port for the diagnostics listener
  3. If binding a privileged port, run with sufficient privileges or pick a port >1024
  4. Retry after a moment if a just-killed process is in TIME_WAIT

Example fix

// before
DiagListener::Tcp(8080)
// after
DiagListener::Tcp(0) // let the OS pick a free ephemeral port, then read listener.local_addr()
Defensive patterns

Strategy: fallback

Validate before calling

// probe the port before binding
if std::net::TcpListener::bind((std::net::Ipv4Addr::LOCALHOST, port)).is_err() {
    eprintln!("port {port} in use; pick another");
}

Type guard

fn port_free(port: u16) -> bool {
    std::net::TcpListener::bind((std::net::Ipv4Addr::LOCALHOST, port)).is_ok()
}

Try / catch

match serve(ctx, DiagListener::Tcp(port)).await {
    Err(e) if e.to_string().contains("bind 127.0.0.1") => {
        eprintln!("diag TCP bind failed on {port}: {e:#}");
        // fallback: retry with port 0 to let the OS choose
        serve(ctx, DiagListener::Tcp(0)).await
    }
    other => other,
}

Prevention

When it happens

Trigger: Starting the diag server with DiagListener::Tcp(port) when the port is already bound by another process, the port is privileged (<1024) without permissions, or ipv4 loopback bind fails for another OS reason.

Common situations: Two diag server instances running concurrently (stale process from a previous session); port collision with another dev service; CI containers reusing a fixed port; configured port 80/443 without root.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/7617439c6129c4af. Report an issue: GitHub.