{"record":{"id":"8f3510fd843a01bd","repo":"Automattic/harper","slug":"unable-to-convert-uri-to-file-path-8f3510","errorCode":null,"errorMessage":"Unable to convert URI to file path.","messagePattern":"Unable to convert URI to file path\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"harper-ls/src/io_utils.rs","lineNumber":14,"sourceCode":"use anyhow::anyhow;\nuse std::path::{Component, PathBuf};\n\nuse tower_lsp_server::{UriExt, lsp_types::Uri};\n\n/// Rewrites a path to a filename using the same conventions as\n/// [Neovim's undo-files](https://neovim.io/doc/user/options.html#'undodir').\npub fn fileify_path(uri: &Uri) -> anyhow::Result<PathBuf> {\n    let mut rewritten = String::new();\n\n    // We assume all URLs are local files and have a base.\n    for seg in uri\n        .to_file_path()\n        .ok_or_else(|| anyhow!(\"Unable to convert URI to file path.\"))?\n        .components()\n    {\n        if !matches!(seg, Component::RootDir) {\n            rewritten.push_str(&seg.as_os_str().to_string_lossy());\n            rewritten.push('%');\n        }\n    }\n\n    Ok(rewritten.into())\n}\n","sourceCodeStart":1,"sourceCodeEnd":25,"githubUrl":"https://github.com/Automattic/harper/blob/5fe7d5ab76492d83f3ecdbc3f1da83c75dcb6f83/harper-ls/src/io_utils.rs#L1-L25","documentation":"harper-ls's `fileify_path` converts an LSP document URI into a filesystem-friendly file name (Neovim undo-file style) by calling `UriExt::to_file_path()` on the URI. That conversion only succeeds for URIs that actually denote local file paths (e.g. `file:///home/user/doc.md`). When the URI cannot be mapped to a file path — it is `None` — the function short-circuits with this anyhow error, since per-client persisted state (ignored lints, per-file dictionaries) would otherwise have nowhere well-defined to live.","triggerScenarios":"Calling `fileify_path` (directly or indirectly via `get_ignored_lints_path` / `get_file_dict_path`) with an LSP `Uri` whose scheme is not `file:` — e.g. `untitled:` buffers, `http(s)://` remote documents, `jdt://`/`zipfile://` virtual schemes, or a malformed URI that `to_file_path()` cannot parse into a local path.","commonSituations":"A client sends `didOpen`/`didChange`/`codeAction` notifications for an unsaved scratch buffer (`untitled:Untitled-1`) or a remotely-hosted/virtual document (remote SSH, browser-based editors, container schemes). Editors like Neovim and VS Code emit such URIs for new or virtual files, and harper-ls then fails while resolving the per-file ignored-lints or dictionary path.","solutions":["Identify the URI in the failing editor notification; it is almost certainly non-`file:` (untitled/virtual/remote document).","Guard in the caller: skip persistence for non-file URIs by checking the scheme (e.g. only proceed when the URI starts with `file:` or when `to_file_path()` succeeds) instead of propagating the error.","If the document is `untitled:`, save it to disk first or configure the editor to assign a real path; harper-ls cannot derive an on-disk path otherwise.","For remote/SSH setups, ensure the language server runs on the same host as the files so the client sends real `file:` URIs.","If you control the code, fall back to a hash of the URI string when a file path cannot be derived, so state can still be persisted per-URI."],"exampleFix":"// before\nlet path = fileify_path(&uri)?;\n\n// after: skip non-file URIs instead of failing\nlet path = match uri.to_file_path() {\n    Some(_) => fileify_path(&uri)?,\n    None => return Ok(None), // untitled/virtual document: no on-disk state\n};","handlingStrategy":"validation","validationCode":"// Rust: validate the URI before calling fileify_path\nfn is_local_file(uri: &lsp_types::Uri) -> bool {\n    uri.as_str().starts_with(\"file:\") || uri.to_file_path().is_some()\n}\n\nif !is_local_file(&uri) {\n    // skip persistence / return early instead of erroring\n}","typeGuard":"// Rust: narrow an arbitrary Uri to a convertible file URI\nfn as_file_path(uri: &Uri) -> Option<PathBuf> {\n    uri.to_file_path()\n}","tryCatchPattern":"// Rust (anyhow): match on the error and degrade gracefully\nmatch fileify_path(&uri) {\n    Ok(p) => get_ignored_lints_path(&p),\n    Err(e) if e.to_string().contains(\"Unable to convert URI to file path\") => {\n        // non-file URI: treat as no persisted state\n        eprintln!(\"skipping state for non-file URI: {uri}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Check the URI scheme (`file:`) before any per-file persistence call in an LSP handler.","Handle `untitled:` buffers explicitly — they never map to a file path.","Run the language server on the same host/filesystem as the edited documents so clients send file URIs.","Never assume all LSP URIs are local files despite the code comment; virtual schemes (jdt, zipfile, http) occur in practice.","Log the offending URI string with the error to make client-side diagnosis immediate."],"tags":["lsp","uri","filesystem","rust"],"backgroundTag":"invalid-url-format","analyzedSha":"5fe7d5ab76492d83f3ecdbc3f1da83c75dcb6f83","analyzedAt":"2026-09-06T11:34:38.610Z","contentChangedAt":"2026-09-06T11:34:38.610Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}