Skip to content

examples/transport tcp example fails every run with connection refused (startup race) #8

Description

@bug-ops

Description

examples/transport/src/tcp.rs's main() spawns the server as a background task and immediately calls client().await with no synchronization between the two:

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tokio::spawn(server());
    client().await?;
    Ok(())
}

async fn server() -> anyhow::Result<()> {
    let tcp_listener = tokio::net::TcpListener::bind("127.0.0.1:8001").await?;
    while let Ok((stream, _)) = tcp_listener.accept().await { ... }
    Ok(())
}

async fn client() -> anyhow::Result<()> {
    let stream = tokio::net::TcpSocket::new_v4()?
        .connect("127.0.0.1:8001".parse()?)
        .await?;
    ...
}

tokio::spawn(server()) only schedules the task; it returns before TcpListener::bind has necessarily run. There is no oneshot channel, retry/backoff, or any other readiness signal before client() connects. In this environment the client task always wins the scheduling race, so the example fails deterministically.

Sibling examples in the same crate use a safer structure: unix_socket.rs calls the synchronous UnixListener::bind directly in main() before spawning the accept loop, guaranteeing the listener exists before the client runs — and it (plus http_upgrade.rs) ran successfully in the same session this was found in.

Reproduction Steps

  1. On main @ 02c62ae (v3.1.2, no drift):
    cargo run --manifest-path examples/transport/Cargo.toml --example tcp
    
  2. Observe: Error: Connection refused (os error 61)
  3. Repeat 3 more times — same failure every time (4/4 failures, lsof -i :8001 confirmed no other process was holding the port).

Expected Behavior

The tcp example demonstrates a working TCP transport round-trip (connect, list tools, call a tool) reliably, the way unix_socket and http_upgrade do.

Actual Behavior

100% failure rate — the client always connects before the server finishes binding its listener.

Environment

  • Version: v3.1.2, commit 02c62ae
  • Platform: macOS (Darwin), local run, no other process on port 8001

Upstream

No exact duplicate found (searched issues and PRs, open and closed, in both bug-ops/rust-sdk and modelcontextprotocol/rust-sdk, with tcp/connection-refused/race keywords).

Spec

.local/specs/004-tcp-example-startup-race/spec.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2High: degraded UX, incorrect non-destructive behaviorbugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions