sinelaw/fresh · error

Server closed connection

Error message

Server closed connection

What it means

run_client_with_connection performs the initial handshake: it writes a hello control message and then reads the server's control reply. If read_control() returns None (the server closed the socket/pipe before sending a response), it raises io::ErrorKind::UnexpectedEof 'Server closed connection'.

Solutions

  1. Verify the server process is running and the connection path (socket/pipe) is correct
  2. Check server logs/stderr for a startup crash or protocol rejection
  3. Remove stale socket files and restart the server
  4. Confirm client and server versions/protocol are compatible
  5. Retry the connection with backoff in case of transient startup timing

Example fix

// before
let server = spawn_server()?;
run_client(&server)?;
// after
let server = spawn_server()?;
wait_until_ready(&server, Duration::from_secs(5))?; // poll health before handshake
run_client(&server)?;
Defensive patterns

Strategy: retry

Validate before calling

fn server_reachable(path: &Path) -> bool { path.exists() && std::os::unix::net::UnixStream::connect(path).is_ok() }

Try / catch

let resp = loop {
    match run_client(&conn) {
        Err(e) if e.kind() == io::ErrorKind::UnexpectedEof && attempts < 3 => { attempts += 1; sleep(backoff); restart_server()?; }
        other => break other,
    }
};

Prevention

When it happens

Trigger: Server process crashes or exits immediately after accepting the connection; wrong socket/pipe path (another process listening that disconnects); server rejects/never answers the hello due to protocol or version mismatch; permission/timeout causing the server to drop the connection.

Common situations: Fresh-editor server binary not running or dying on startup (bad config, port/pipe conflicts, missing permissions); version mismatch between client and server leading the server to close early; connecting to a stale socket file left from a previous run.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13). Data as JSON: /api/errors/2ce6a0571e06a532. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-editor/src/client/mod.rs:79

/// Run the client with an already-established connection
///
/// This is useful when the caller has already established a connection
/// (e.g., after retrying connection attempts). Performs handshake then relay.
pub fn run_client_with_connection(
    config: ClientConfig,
    conn: ClientConnection,
) -> io::Result<ClientExitReason> {
    // Perform handshake
    let hello = ClientHello::new(config.term_size);
    let hello_json = serde_json::to_string(&ClientControl::Hello(hello))
        .map_err(|e| io::Error::other(e.to_string()))?;
    conn.write_control(&hello_json)?;

    // Read server response
    let response = conn
        .read_control()?
        .ok_or_else(|| io::Error::new(io::ErrorKind::UnexpectedEof, "Server closed connection"))?;

    let server_msg: ServerControl =
        serde_json::from_str(&response).map_err(|e| io::Error::other(e.to_string()))?;

    match server_msg {
        ServerControl::Hello(server_hello) => {
            if server_hello.protocol_version != PROTOCOL_VERSION {
                return Ok(ClientExitReason::VersionMismatch {
                    server_version: server_hello.server_version,
                });
            }
            tracing::info!(
                "Connected to session '{}' (server {})",
                server_hello.session_id,
                server_hello.server_version
            );
        }
        ServerControl::VersionMismatch(mismatch) => {

View on GitHub (pinned to 67894ca546)