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
- Verify the server process is running and the connection path (socket/pipe) is correct
- Check server logs/stderr for a startup crash or protocol rejection
- Remove stale socket files and restart the server
- Confirm client and server versions/protocol are compatible
- 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
- Wait for a server readiness signal (health file/pipe) before handshaking
- Log server stderr to diagnose crashes at startup
- Clean up stale socket files before connecting
- Pin client/server protocol versions and check them early
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
- handshake with the Fresh editor failed
- server closed the connection before answering
- Server closed connection during handshake
- Server error
- Unexpected server response
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)