sinelaw/fresh · error

Server error

Error message

Server error: {}

What it means

During the CLI handshake in main.rs, the server replied with the `ServerControl::Error { message }` variant. Fresh forwards the server's own error text verbatim via `"Server error: {}"`, so the root cause is whatever the editor reported (bad session, rejected command, internal failure). The CLI cannot proceed past the handshake when the server rejects it.

Solutions

  1. Read the embedded message after 'Server error:' — it is the editor's own diagnostic and names the real problem.
  2. Check the editor's logs for the matching server-side error to get the stack/cause.
  3. Retry the same CLI command after fixing whatever the embedded message indicates.
  4. If the message indicates a protocol/feature issue, upgrade client and editor to matching versions.

Example fix

// before: treating the wrapper as the cause
match result { Err(e) => eprintln!("failed: {e}"), ... }
// after: surface the inner server message for tooling
if let Some(msg) = e.to_string().strip_prefix("Server error: ") {
    eprintln!("editor rejected request: {msg}");
}
Defensive patterns

Strategy: try-catch

Try / catch

match run_handshake() {
    Err(e) if e.to_string().starts_with("Server error: ") => {
        let detail = &e.to_string()["Server error: ".len()..];
        eprintln!("editor rejected: {detail}");
    }
    Err(e) => return Err(e),
    Ok(v) => Ok(v),
}

Prevention

When it happens

Trigger: Any handshake where the connected editor responds to the client Hello with `ServerControl::Error`; the message embedded in the error is produced server-side.

Common situations: Requesting an operation the running editor rejects; server hit an internal error while setting up the session; protocol accepted but the requested session/file failed validation on the server.

Related errors


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

Appendix: source

Thrown at crates/fresh-editor/src/main.rs:3369

    let response = read_reply()?
        .ok_or_else(|| anyhow::anyhow!("Server closed connection during handshake"))?;

    match serde_json::from_str::<ServerControl>(&response)? {
        ServerControl::Hello(server_hello) => {
            if server_hello.protocol_version != PROTOCOL_VERSION {
                eprintln!(
                    "Version mismatch: server is v{}",
                    server_hello.server_version
                );
                return Ok(false);
            }
            Ok(true)
        }
        ServerControl::VersionMismatch(mismatch) => {
            eprintln!("Version mismatch: server is v{}", mismatch.server_version);
            Ok(false)
        }
        ServerControl::Error { message } => Err(anyhow::anyhow!("Server error: {}", message)),
        _ => Err(anyhow::anyhow!("Unexpected server response")),
    }
}

/// When launched from inside Fresh's own embedded terminal, forward the
/// file/dir arguments to the parent editor (identified by `FRESH_SESSION`)
/// instead of starting a second editor in the terminal.
///
/// Returns:
/// - `Some(Ok(()))` — forwarded; the caller should exit.
/// - `None` — not a nested launch, nothing to forward, or the parent
///   socket is unreachable; the caller should launch inline as usual.
fn try_forward_nested(args: &Args) -> Option<AnyhowResult<()>> {
    // Only plain interactive file/dir opens are forwarded. Subcommands,
    // --server and --attach are already handled before we get here;
    // --stdin pipes content into a real editor and can't be forwarded.
    if args.server || args.attach || args.stdin || args.files.is_empty() {
        return None;

View on GitHub (pinned to 67894ca546)