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

  1. Check what URI the LSP server returned (log the LspUri before calling open_lsp_uri_target) and confirm it uses the file:// scheme
  2. If the server emits virtual-document schemes, add a handler for that scheme instead of routing through open_lsp_uri_target
  3. Verify the authority's path_translation config is intact; a broken translation table can make decoding fail
  4. 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

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


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 path

View on GitHub (pinned to 67894ca546)