sinelaw/fresh · error
cannot reach the Fresh editor for session
Error message
cannot reach the Fresh editor for session '{}': its control socket ({}) could not be reached because connecting to it was denied. The editor is most likely running — the socket simply lives outside this process's sandbox. Re-run this command outside the sandbox. What it means
Produced by `socket_denied_error` when connecting to the session's control socket fails with a permission/denied error: the socket file exists and the editor is almost certainly alive, but this process's sandbox (e.g. an agent sandbox) forbids reaching it. Fresh deliberately worded the message as an actionable instruction for automated agents: re-run the command outside the sandbox.
Solutions
- Re-run the command outside the sandbox, as the message instructs.
- If you control the sandbox policy, allow connect access to the control socket path (socket_paths.control).
- Verify user/permissions on the socket file if not sandbox-related (ls -l, chown/chmod as needed).
Example fix
// before (agent sandboxed, fails) // inside sandbox: fresh-cli open foo.rs // after: escape the sandbox for this one command // $ fresh-cli open foo.rs (run in the host shell, not the sandboxed tool)
Defensive patterns
Strategy: fallback
Validate before calling
fn can_reach_socket(p: &std::path::Path) -> bool {
std::os::unix::net::UnixStream::connect(p).is_ok()
} Try / catch
match run_cmd() {
Err(e) if e.to_string().contains("connecting to it was denied") => {
eprintln!("hint: re-run outside the sandbox");
std::process::exit(2);
}
other => other?,
} Prevention
- Detect sandboxed environments (env markers) before invoking Fresh CLI against a live session
- Document the outside-the-sandbox requirement in agent instructions
- Grant sandbox policy exceptions for the control socket path if you own the sandbox
- Check socket file permissions when not sandbox-related
When it happens
Trigger: Calling any Fresh CLI command that targets an existing session (`FRESH_SESSION`) from inside a sandboxed process whose connect() to the control socket path is denied by the sandbox policy.
Common situations: Running CLI commands from within an AI coding agent's sandboxed shell; container/seccomp/landlock rules blocking unix-socket connect; running as a different user without rights to the socket file.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Server closed connection during handshake
- no running Fresh editor for session
- handshake with the Fresh editor failed
- server error
- server closed the connection before answering
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/4be26dafdd3ad2f0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-editor/src/main.rs:3532
rest.push(tokens[i]);
i += 1;
}
(session, rest)
}
/// Resolve the control socket for a command-channel verb.
///
/// The default target is the current workspace, named by `$FRESH_SESSION`;
/// `--session <id>` overrides it. Unlike `run_open_files_command`, this never
/// spawns a daemon — these verbs only make sense against a live editor, so a
/// missing session or dead server is a hard error.
/// The error for "the socket is there, but this process may not reach it".
///
/// Worded so the reader can act: an agent that can re-run outside its sandbox
/// needs to be told that is the fix, and told it in terms it can match on. The
/// injected "Teach Fresh CLI" contract points at this phrasing.
fn socket_denied_error(session: &str, socket_paths: &SocketPaths) -> anyhow::Error {
anyhow::anyhow!(
"cannot reach the Fresh editor for session '{}': its control socket ({}) \
could not be reached because connecting to it was denied. The editor is \
most likely running — the socket simply lives outside this process's \
sandbox. Re-run this command outside the sandbox.",
session,
socket_paths.control.display(),
)
}
fn resolve_cmd_socket(session_override: Option<&str>) -> AnyhowResult<SocketPaths> {
let session = match session_override {
Some(s) if !s.trim().is_empty() => s.to_string(),
_ => match std::env::var("FRESH_SESSION") {
Ok(s) if !s.trim().is_empty() => s,
_ => anyhow::bail!(
"not inside a Fresh session; set --session <id> (or run inside a \
Fresh workspace so $FRESH_SESSION is set)"
),View on GitHub (pinned to 67894ca546)