{"record":{"id":"1501d8af820a67d1","repo":"gitbutlerapp/gitbutler","slug":"invalid-path-scheme","errorCode":null,"errorMessage":"Invalid path scheme: {}","messagePattern":"Invalid path scheme: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-api/src/open/mod.rs","lineNumber":58,"sourceCode":"/// be converted to a path, or every available opener command fails to launch.\npub(crate) fn open_that(target_url: &Url) -> anyhow::Result<()> {\n    if ![\n        \"http\",\n        \"https\",\n        \"mailto\",\n        \"vscode\",\n        \"vscodium\",\n        \"vscode-insiders\",\n        \"zed\",\n        \"windsurf\",\n        \"cursor\",\n        \"trae\",\n        \"antigravity-ide\",\n        \"file\",\n    ]\n    .contains(&target_url.scheme())\n    {\n        bail!(\"Invalid path scheme: {}\", target_url.scheme());\n    }\n\n    if open_editor_url_as_command_invocation_on_wsl(target_url) {\n        return Ok(());\n    }\n\n    fn clean_env_vars<'a, 'b>(\n        var_names: &'a [&'b str],\n    ) -> impl Iterator<Item = (&'b str, String)> + 'a {\n        var_names\n            .iter()\n            .filter_map(|name| env::var(name).map(|value| (*name, value)).ok())\n            .map(|(name, value)| {\n                (\n                    name,\n                    value\n                        .split(':')\n                        .filter(|path| {","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-api/src/open/mod.rs#L40-L76","documentation":"open_that (crates/but-api/src/open/mod.rs:41-59) only opens URLs whose scheme is in its allowlist: http, https, mailto, vscode, vscodium, vscode-insiders, zed, windsurf, cursor, trae, antigravity-ide, and file. Any other scheme bails immediately with this message before any opener command runs.","triggerScenarios":"Routing links like ssh://git@..., jetbrains://..., slack://..., or a typo'd scheme (htps://) through GitButler's open-URL API; a new editor scheme that has not been added to the allowlist.","commonSituations":"Apps embedding GitButler's opener for arbitrary clicked links; users on IDEs whose URL scheme (e.g. jetbrains://) is not in the list; scheme casing or whitespace quirks in constructed URLs.","solutions":["Use a supported scheme: http/https/mailto/file or one of the listed editor schemes (vscode, cursor, zed, windsurf, ...)","Open unsupported URLs with the OS opener directly instead of this API","For a genuinely needed scheme, extend the allowlist in open_that and ship it, rather than working around at call sites"],"exampleFix":"// before\nopenThat(new URL('jetbrains://open?file=x.ts'));\n// error: Invalid path scheme: jetbrains\n\n// after\nopenThat(new URL('vscode://file/x.ts')); // allowed editor scheme","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"const OPENABLE_SCHEMES = new Set([\n  'http', 'https', 'mailto', 'file',\n  'vscode', 'vscodium', 'vscode-insiders',\n  'zed', 'windsurf', 'cursor', 'trae', 'antigravity-ide',\n]);\n\nfunction isOpenableScheme(url: URL): boolean {\n  return OPENABLE_SCHEMES.has(url.protocol.replace(':', '').toLowerCase());\n}","tryCatchPattern":"try {\n  await openThat(url);\n} catch (e) {\n  if (String(e).startsWith('Invalid path scheme')) {\n    // open with the OS shell opener directly instead\n  } else throw e;\n}","preventionTips":["Validate the scheme against the allowlist before calling the opener","Handle unknown-scheme links in the host app (shell.openExternal or equivalent)","Lowercase and trim schemes before comparison; scheme comparison is case-sensitive in the backend list check"],"tags":["url","scheme","validation","opener","rust"],"backgroundTag":"unsupported-url-scheme","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}