{"record":{"id":"487a7db0a36c84d9","repo":"sinelaw/fresh","slug":"cannot-mix-local-and-remote-files-use-either-local-paths-or-487a7d","errorCode":null,"errorMessage":"Cannot mix local and remote files. Use either local paths or remote paths (user@host:path).","messagePattern":"Cannot mix local and remote files\\. Use either local paths or remote paths \\(user@host:path\\)\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fresh-editor/src/main.rs","lineNumber":1786,"sourceCode":"        let first = remote_locations[0];\n        for r in &remote_locations[1..] {\n            if r.user != first.user || r.host != first.host {\n                anyhow::bail!(\n                    \"Cannot open files from multiple remote hosts. \\\n                     First: {}@{}, found: {}@{}\",\n                    first.user,\n                    first.host,\n                    r.user,\n                    r.host\n                );\n            }\n        }\n        // Check that there are no local files mixed with remote\n        let has_local = parsed_locations\n            .iter()\n            .any(|loc| matches!(loc, ParsedLocation::Local(_)));\n        if has_local {\n            anyhow::bail!(\n                \"Cannot mix local and remote files. Use either local paths or remote paths (user@host:path).\"\n            );\n        }\n        Some(first.clone())\n    } else {\n        None\n    };\n\n    // Convert to FileLocation for downstream code\n    let file_locations: Vec<FileLocation> = parsed_locations\n        .into_iter()\n        .map(|loc| match loc {\n            ParsedLocation::Local(fl) => fl,\n            ParsedLocation::Remote(rl) => FileLocation {\n                path: PathBuf::from(&rl.path),\n                line: rl.line,\n                column: rl.column,\n                end_line: None,","sourceCodeStart":1768,"sourceCodeEnd":1804,"githubUrl":"https://github.com/sinelaw/fresh/blob/67894ca5463dbd7a89bb31add4627c27d6b79d83/crates/fresh-editor/src/main.rs#L1768-L1804","documentation":"Fresh rejects a command line that combines plain local paths with remote user@host:path paths. One editor invocation must be purely local or purely remote, because the session backing store (single local process vs one SSH host) is chosen up front. The check runs when at least one remote location was parsed and any parsed location is Local.","triggerScenarios":"`fresh notes.txt alice@host:notes.txt` — any mix of a bare path and a user@host:path on the same command line.","commonSituations":"Opening today's local scratch file alongside a remote log; tab-completion producing a local path next to a remote one; wrapper scripts that append paths without checking their origin.","solutions":["Remove the local paths and open only remote paths in this invocation.","Open the local files in a separate local Fresh invocation.","Pull the remote file locally (scp/rsync) if you truly need them in one window."],"exampleFix":"// before\nfresh ./README.md alice@host:/var/log/syslog\n// after\nfresh ./README.md\nfresh alice@host:/var/log/syslog   # separate remote invocation","handlingStrategy":"validation","validationCode":"const isRemote = a => /^[^@\\s]+@[^:\\s]+:/.test(a);\nconst remotes = args.filter(isRemote);\nconst locals = args.filter(a => !isRemote(a));\nif (remotes.length && locals.length) {\n  throw new Error('Pass local and remote paths in separate fresh invocations');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Decide local-vs-remote per invocation, not per file.","Wrap fresh in a helper that partitions arguments and issues separate calls.","Watch tab-completion output for accidental local paths next to remote ones."],"tags":["cli","remote","argument-validation"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"67894ca5463dbd7a89bb31add4627c27d6b79d83","analyzedAt":"2026-09-13T15:04:03.701Z","contentChangedAt":"2026-09-13T15:04:03.701Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}