sinelaw/fresh · error
handshake with the Fresh editor failed
Error message
handshake with the Fresh editor failed
What it means
After connecting to the editor's control socket, the client performs a protocol handshake via client_handshake. If the editor does not accept (protocol/version or capability mismatch), the client bails with this generic message; the specific mismatch reason was already printed by client_handshake to stderr.
Solutions
- Read the mismatch reason printed just before this error on stderr.
- Restart the editor so both sides run the same Fresh version.
- Ensure the `fresh` on PATH is the same build as the running editor (`which fresh`, `fresh --version`).
- Reinstall/upgrade so client and editor versions match.
Example fix
# before ~/.cargo/bin/fresh --cmd split right # old client vs new editor # after hash -r; fresh --version && fresh --cmd split right # same binary as running editor
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure client and editor versions match before connecting
const { execSync } = require('child_process');
const cliV = execSync('fresh --version').toString().trim();
const srvV = execSync('fresh --cmd version').toString().trim();
if (cliV !== srvV) { console.error(`version mismatch: cli=${cliV} editor=${srvV}; restart editor`); process.exit(1); } Try / catch
// rust
match connect_and_handshake(&paths) {
Ok(conn) => conn,
Err(e) if e.to_string().contains("handshake") => {
eprintln!("version mismatch with running editor; restart it and retry");
std::process::exit(2);
}
Err(e) => return Err(e),
} Prevention
- Keep one fresh binary on PATH; avoid mixing cargo-install and distro builds.
- Restart the editor after upgrading Fresh.
- Pin the editor and CLI to the same version in scripts.
- Read the mismatch details printed before this generic error.
When it happens
Trigger: A fresh CLI client whose protocol version/capabilities differ from the running editor's, so the editor rejects the CmdConnection during handshake.
Common situations: Editor upgraded (or downgraded) while an old binary is still running and newer/older CLI invokes it; mixing two Fresh versions on PATH (e.g. one from cargo install, one distro package); connecting a stale CLI to a long-lived editor session.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Server error
- Unexpected server response
- no running Fresh editor for session
- server error
- server closed the connection before answering
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/33ed296bfb0f1137.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-editor/src/main.rs:3672
let conn = fresh::server::ipc::ClientConnection::connect(socket_paths).map_err(|e| {
if e.kind() == std::io::ErrorKind::PermissionDenied {
socket_denied_error(
&std::env::var("FRESH_SESSION").unwrap_or_else(|_| "?".to_string()),
socket_paths,
)
} else {
anyhow::Error::from(e)
}
})?;
let mut reader = conn.control_reader();
let accepted = client_handshake_reading(&conn, || {
reader
.read_line_timeout(cmd_reply_timeout())
.map_err(cmd_read_error)
})?;
if !accepted {
// client_handshake already printed the mismatch reason.
anyhow::bail!("handshake with the Fresh editor failed");
}
Ok(CmdConnection { conn, reader })
}
/// Read control replies until a `ScriptResult` arrives (or an error/EOF/timeout).
/// Returns `(ok, error, output)`.
fn read_script_result(
conn: &mut CmdConnection,
timeout: std::time::Duration,
) -> AnyhowResult<(bool, Option<String>, Option<String>)> {
use fresh::server::protocol::ServerControl;
loop {
match conn.recv_within(timeout)? {
Some(ServerControl::ScriptResult { ok, error, output }) => {
return Ok((ok, error, output))
}
Some(ServerControl::Error { message }) => anyhow::bail!("server error: {}", message),
Some(_) => continue,View on GitHub (pinned to 67894ca546)