sinelaw/fresh · error

Cannot open files from multiple remote hosts. First

Error message

Cannot open files from multiple remote hosts. First: {}@{}, found: {}@{}

What it means

All remote file arguments in one invocation must come from the same user@host:port. When the parsed remote locations differ in user, host, or port, the parser bails naming the first and the conflicting remote. A single editor session can only attach to one remote host.

Solutions

  1. Run one editor instance per remote host
  2. Open remote files in separate invocations or editor tabs/sessions
  3. Normalize the user@host:port so all files reference the same remote
  4. If multi-host is genuinely needed, use a session manager or split into multiple terminal windows

Example fix

// before
fresh alice@build.example.com:/etc/app.conf root@build.example.com:/etc/db.conf
// after
fresh alice@build.example.com:/etc/app.conf
fresh root@build.example.com:/etc/db.conf  # separate invocation
Defensive patterns

Strategy: validation

Validate before calling

let remotes: Vec<&str> = args.iter().filter(|a| a.contains('@') || a.starts_with("ssh://")).map(|a| a.as_str()).collect();
let hosts: std::collections::HashSet<_> = remotes.iter().map(|r| r.split('@').last().unwrap_or(r).split(':').next().unwrap_or(r)).collect();
if hosts.len() > 1 { eprintln!("files span multiple remote hosts"); }

Type guard

fn is_remote_spec(s: &str) -> bool { s.starts_with("ssh://") || (s.contains('@') && s.contains(':')) }

Try / catch

match editor::launch(args) {
    Err(e) if e.to_string().contains("multiple remote hosts") => eprintln!("open one host per session"),
    Err(e) => eprintln!("{e}"),
    Ok(()) => {},
}

Prevention

When it happens

Trigger: Invoking e.g. `fresh ssh://alice@host1:/a.txt bob@host2:/b.txt` or `fresh alice@host1:/a.txt root@host1:/b.txt` — parsed remotes[1..] differ from remotes[0] in user, host, or port.

Common situations: Mixing files from dev and staging servers in one command; forgetting a username differs (alice@ vs root@ on same host); different ports (host:2222 vs host) counting as different remotes.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

        .map(|f| parse_location(f))
        .collect::<AnyhowResult<Vec<_>>>()?;

    let remotes: Vec<&RemoteLocation> = parsed
        .iter()
        .filter_map(|loc| match loc {
            ParsedLocation::Remote(r) => Some(r),
            ParsedLocation::Local(_) => None,
        })
        .collect();

    if remotes.is_empty() {
        return Ok(None);
    }

    let first = remotes[0];
    for r in &remotes[1..] {
        if r.user != first.user || r.host != first.host || r.port != first.port {
            anyhow::bail!(
                "Cannot open files from multiple remote hosts. First: {}@{}, found: {}@{}",
                first.user,
                first.host,
                r.user,
                r.host
            );
        }
    }
    if parsed
        .iter()
        .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)))

View on GitHub (pinned to 67894ca546)