sinelaw/fresh · error
URI is not a file path
Error message
URI is not a file path
What it means
open_lsp_uri_target converts an LSP document URI (e.g. file:///src/main.rs) into a host-filesystem path via LspUri::to_host_path, applying the active authority's path translation (local or devcontainer). When the URI cannot be decoded into a file path (wrong scheme like untitled: or a malformed URI), to_host_path returns None and this anyhow error is thrown instead of opening a buffer.
Solutions
- Check what URI the LSP server returned (log the LspUri before calling open_lsp_uri_target) and confirm it uses the file:// scheme
- If the server emits virtual-document schemes, add a handler for that scheme instead of routing through open_lsp_uri_target
- Verify the authority's path_translation config is intact; a broken translation table can make decoding fail
- Reject non-file URIs early in handle_goto_definition_response with a user-facing message instead of a hard error
Example fix
// before
let buffer_id = self.open_lsp_uri_target(&uri)?;
// after
if uri.to_host_path(self.authority().path_translation.as_ref()).is_none() {
log::warn!("goto-def returned non-file URI: {}", uri.as_str());
return Ok(());
}
let buffer_id = self.open_lsp_uri_target(&uri)?; Defensive patterns
Strategy: validation
Validate before calling
fn is_file_uri(uri: &LspUri, translation: Option<&PathTranslation>) -> bool {
uri.to_host_path(translation).is_some()
}
// call open_lsp_uri_target only if is_file_uri(...) Type guard
fn as_host_path<'a>(uri: &'a LspUri, t: Option<&PathTranslation>) -> Option<PathBuf> {
uri.to_host_path(t)
} Try / catch
match self.open_lsp_uri_target(&uri) {
Ok(id) => /* focus buffer id */,
Err(e) if e.to_string().contains("URI is not a file path") => log::warn!("non-file LSP URI skipped"),
Err(e) => return Err(e),
} Prevention
- Always check to_host_path for None before requesting a buffer open for an LSP URI
- Log incoming goto-definition URIs during language-server integration testing
- Treat non-file:// schemes (untitled:, jdt:) as separate features with dedicated handlers
- Keep the authority's path_translation table validated at startup
When it happens
Trigger: A goto-definition response from the LSP server returns a URI whose scheme is not file:// (e.g. untitled:, jdt://, or an opaque URI), or a file:// URI whose path component is empty/malformed so to_host_path yields None.
Common situations: Language servers that report definitions in virtual documents (Java's jdt://, Rust-analyzer's occasionally synthesized URIs); a misconfigured server returning non-file URIs; LSP servers from other tools sending custom schemes the editor doesn't map.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- URI is not a file path (container-side decode failed)
- could not open : file not found
- Buffer not found
- active window present
- LSP server for ' ' is unavailable
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/e12dddd47523d2e2.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-editor/src/app/file_open_orchestrators.rs:686
/// `~/.local/.../site-packages/flask/app.py`): fetch the file
/// bytes via the authority's process spawner
/// (`docker exec <id> cat <path>`) and open them as a
/// read-only buffer at the in-container path.
/// * **unreachable** (no file at the host path; container fetch
/// failed or no container authority): return `Err` so the
/// caller can surface a user-visible status message instead
/// of silently opening a phantom buffer.
///
/// Cursor placement, focus, and any post-open hook firing are the
/// caller's job (this method just resolves "URI → BufferId").
pub(crate) fn open_lsp_uri_target(
&mut self,
uri: &crate::app::types::LspUri,
) -> anyhow::Result<BufferId> {
let translation = self.authority().path_translation.clone();
let host_path = uri
.to_host_path(translation.as_ref())
.ok_or_else(|| anyhow::anyhow!("URI is not a file path"))?;
// Case 1: file is reachable on the host filesystem (either
// local authority, or workspace-mounted on a devcontainer).
// `open_file` focuses, which is what callers (goto-def,
// workspace edits) expect — they want the cursor to land in
// the destination buffer afterward.
if self.authority().filesystem.exists(&host_path) {
return self.open_file(&host_path);
}
// Case 2: container-only fetch. Only meaningful when the
// active authority can route a `cat` through to the
// container — `path_translation` being set is the proxy for
// "this is a container authority". Local + SSH authorities
// skip straight to the error case.
if translation.is_some() {
// The container-side path is the URI's raw path. Calling
// `to_host_path` with `None` returns the wire-side pathView on GitHub (pinned to 67894ca546)