BloopAI/vibe-kanban · error

client server address already set

Error message

client server address already set

What it means

set_server_addr stores the listener's local address in a once-only (OnceCell-style) field of client_info. The .expect("client server address already set") panics if the address was already initialized earlier in the process. It is an internal invariant check: server_addr must be set exactly once during startup.

Source

Thrown at crates/server/src/main.rs:136

    let actual_main_port = main_listener.local_addr()?.port();

    let proxy_listener = tokio::net::TcpListener::bind(format!("{host}:{proxy_port}")).await?;
    let actual_proxy_port = proxy_listener.local_addr()?.port();

    if let Err(e) = write_port_file_with_proxy(actual_main_port, Some(actual_proxy_port)).await {
        tracing::warn!("Failed to write port file: {}", e);
    }

    tracing::info!(
        "Main server on :{}, Preview proxy on :{}",
        actual_main_port,
        actual_proxy_port
    );

    deployment
        .client_info()
        .set_server_addr(main_listener.local_addr()?)
        .expect("client server address already set");
    deployment
        .client_info()
        .set_preview_proxy_port(actual_proxy_port)
        .expect("client preview proxy port already set");

    let app_router = routes::router(deployment.clone());

    // Production only: open browser
    if !cfg!(debug_assertions) {
        tracing::info!("Opening browser...");
        let browser_port = actual_main_port;
        tokio::spawn(async move {
            if let Err(e) =
                utils::browser::open_browser(&format!("http://127.0.0.1:{browser_port}")).await
            {
                tracing::warn!(
                    "Failed to open browser automatically: {}. Please open http://127.0.0.1:{} manually.",
                    e,

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Verify set_server_addr is called exactly once in the startup path; remove duplicate calls.
  2. If the address may legitimately change, replace once-only semantics with set/replace (RwLock/Mutex) or .ok() instead of expect.
  3. Check that no retry/test wrapper re-runs the initialization block within the same process.

Example fix

// before
deployment.client_info().set_server_addr(main_listener.local_addr()?)
    .expect("client server address already set");
// after
deployment.client_info()
    .set_server_addr(main_listener.local_addr()?)
    .unwrap_or_else(|_| tracing::warn!("server addr already set; keeping existing value"));
Defensive patterns

Strategy: validation

Validate before calling

if deployment.client_info().server_addr().is_none() {
    deployment.client_info()
        .set_server_addr(main_listener.local_addr()?)?;
}

Try / catch

deployment.client_info()
    .set_server_addr(main_listener.local_addr()?)
    .unwrap_or_else(|_| tracing::debug!("server addr already configured"));

Prevention

When it happens

Trigger: DeploymentImpl::new (or another startup path) already called client_info().set_server_addr() before line 136, so the second call finds the cell occupied and panics. Only reachable if startup code is duplicated or reordered.

Common situations: Modifying main.rs so initialization runs twice (re-created deployment, retried listener setup); merging code that sets the address in both a fallback and main path; refactoring that moved DeploymentImpl::new after address configuration.

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/72fe10f33f994b11. Report an issue: GitHub.