sinelaw/fresh · error
--ssh-url expects an ssh:// URL, got
Error message
--ssh-url expects an ssh:// URL, got {:?} What it means
parse_ssh_url_arg validates the --ssh-url CLI argument passed by spawn_server_detached when re-invoking the editor for a remote session. The URL must start with ssh://; anything else is a hard error because the argument is generated internally — a malformed value means the URL was corrupted between the two processes.
Solutions
- Check the exact value passed to --ssh-url; it must literally begin with ssh://
- If a user typo'd it, use the correct form: --ssh-url ssh://user@host/path
- If spawn_server_detached produced it, debug the argument construction/quoting there — this is an internal corruption per the doc comment
- Log the full argv at spawn time to catch mangling by intermediate shells
Example fix
// before fresh --ssh-url host.example.com/repo // after fresh --ssh-url ssh://host.example.com/repo
Defensive patterns
Strategy: validation
Validate before calling
fn valid_ssh_url_arg(url: &str) -> bool {
url.starts_with("ssh://")
}
// assert before spawning: debug_assert!(valid_ssh_url_arg(&built_url)); Try / catch
match parse_ssh_url_arg(&arg) {
Err(e) if e.to_string().starts_with("--ssh-url expects") => {
eprintln!("internal error: malformed --ssh-url '{arg}'; expected ssh://…");
std::process::exit(2);
}
other => other?,
} Prevention
- Always build the --ssh-url value with the ssh:// prefix in spawn_server_detached
- Log full argv at re-exec time to catch quoting/word-splitting mangling
- Quote the URL argument when constructing the re-exec command line
- Never hand-edit the generated --ssh-url; let spawn_server_detached construct it
When it happens
Trigger: The editor re-executes itself with --ssh-url and the argument lost its ssh:// prefix — quoting/word-splitting issues in the spawn command, shell interpolation mangling the URL, or a user manually invoking the binary with a bad --ssh-url value.
Common situations: Manual testing of the binary with --ssh-url host:path instead of ssh://host/path; a bug in spawn_server_detached that truncates or rewrites the argument; special characters in host/user names breaking quoting.
Understand the failure class
Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.
Related errors
- Cannot open files from multiple remote hosts. First
- Cannot mix local and remote files. Use either local paths…
- --ssh-url is not a valid ssh:// URL
- invalid ssh:// URL : . To open a local file with this name…
- Too many '+ ' arguments (at most one is allowed)
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/6b82cc849fa117c4.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-editor/src/main.rs:1456
.any(|loc| matches!(loc, ParsedLocation::Local(_)))
{
anyhow::bail!(
"Cannot mix local and remote files. Use either local paths or remote paths (ssh:// or user@host:path)."
);
}
Ok(Some(remote_location_to_ssh_url(first)))
}
/// Parse a standalone `ssh://…` URL passed via the internal
/// `--ssh-url` flag. Accepts only the URL form (not scp-style) and
/// the URL must carry a path; anything else is a hard error because
/// this input comes from our own `spawn_server_detached` and a
/// malformed URL there means we corrupted it on the way over.
fn parse_ssh_url_arg(url: &str) -> AnyhowResult<RemoteLocation> {
let rest = url
.strip_prefix("ssh://")
.ok_or_else(|| anyhow::anyhow!("--ssh-url expects an ssh:// URL, got {:?}", url))?;
parse_ssh_url_rest(rest, default_ssh_user().as_deref()).map_err(|reason| {
anyhow::anyhow!(
"--ssh-url is not a valid ssh:// URL ({}): {:?}",
reason,
url
)
})
}
/// Parse a location that may be local, scp-style remote, or an
/// `ssh://` URL.
///
/// Accepted forms:
/// - local: `file`, `file:line`, `file:line:col`
/// - scp-style remote: `user@host:path[:line[:col]]`
/// - URL-style remote: `ssh://[user@]host[:port]/path[:line[:col]]`
///
/// When `ssh://` omits the user, the current login name (`$USER` /View on GitHub (pinned to 67894ca546)