{"record":{"id":"ac86b2322a366739","repo":"denoland/deno","slug":"invalid-deep-link-scheme-scheme-reason","errorCode":null,"errorMessage":"Invalid deep-link scheme {scheme:?}: {reason}.","messagePattern":"Invalid deep-link scheme (.+?): (.+?)\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/tools/desktop.rs","lineNumber":690,"sourceCode":"      }\n    }\n  };\n  match target_os {\n    \"macos\" => make_self_extracting_macos(bundle_path, format, desktop_flags),\n    \"windows\" => make_self_extracting_dir(bundle_path, format, true),\n    _ => make_self_extracting_dir(bundle_path, format, false),\n  }\n}\n\n/// Validate a deep-link URL scheme. Follows the RFC 3986 `scheme` grammar:\n/// `ALPHA *( ALPHA / DIGIT / \"+\" / \"-\" / \".\" )`. We additionally reject the\n/// common reserved schemes (`http`, `https`, `file`, `ftp`, `ws`, `wss`) since\n/// registering those as app handlers is almost never intended and would hijack\n/// normal browsing.\nfn validate_url_scheme(scheme: &str) -> Result<(), AnyError> {\n  let reserved = [\"http\", \"https\", \"file\", \"ftp\", \"ws\", \"wss\"];\n  let bail = |reason: &str| {\n    Err(deno_core::anyhow::anyhow!(\n      \"Invalid deep-link scheme {scheme:?}: {reason}.\"\n    ))\n  };\n  match scheme.chars().next() {\n    None => return bail(\"scheme is empty\"),\n    Some(c) if !c.is_ascii_alphabetic() => {\n      return bail(\"scheme must start with an ASCII letter\");\n    }\n    _ => {}\n  }\n  if !scheme\n    .chars()\n    .all(|c| c.is_ascii_alphanumeric() || matches!(c, '+' | '-' | '.'))\n  {\n    return bail(\"scheme may only contain letters, digits, '+', '-', and '.'\");\n  }\n  if reserved.contains(&scheme) {\n    return bail(\"scheme is reserved and cannot be used as a deep link\");","sourceCodeStart":672,"sourceCodeEnd":708,"githubUrl":"https://github.com/denoland/deno/blob/f7822238cab635a3a19f99f493f675fa81a7f9d8/cli/tools/desktop.rs#L672-L708","documentation":"Thrown by validate_url_scheme in cli/tools/desktop.rs when a deep-link scheme passed via `deno desktop --deep-link <scheme>` (or the config file `deep_links` array) violates the RFC 3986 scheme grammar enforced by the tool: it must start with an ASCII letter, may contain only letters/digits/'+'/'-'/'.', and must not be one of the reserved schemes http/https/file/ftp/ws/wss. The {reason} clause names the exact rule that failed. Reserved schemes are rejected because registering them as app handlers would hijack normal browsing.","triggerScenarios":"Running `deno desktop --deep-link \"acme://app\" main.ts` (full URL passed instead of the bare scheme), `--deep-link 1app` (starts with a digit), `--deep-link my_app` (underscore is not in the allowed set), `--deep-link \"my app\"` (whitespace), `--deep-link https` (reserved), or an empty/blank entry coming from a config file `deep_links` array.","commonSituations":"Copying a full URL like `myapp://callback` from OAuth docs and pasting it as the scheme; including the trailing colon or `://`; using `https` or `file` because the app also handles web links; underscored names common in package names (my_app) which are legal elsewhere but not in URI schemes; whitespace sneaking in from a JSON config or shell quoting.","solutions":["Pass only the bare scheme without `://` or colon, e.g. `--deep-link acme` instead of `--deep-link \"acme://app\"`.","Make the scheme start with an ASCII letter and contain only letters, digits, '+', '-', '.' — replace `my_app` with `my-app`.","Pick a custom scheme (e.g. `acme`, `com.acme.app`) instead of a reserved one (http, https, file, ftp, ws, wss).","Trim whitespace and remove empty strings from the `deep_links` entries in your deno config file."],"exampleFix":"# before\ndeno desktop --deep-link \"acme://login\" --deep-link my_app main.ts\n\n# after\ndeno desktop --deep-link acme --deep-link my-app main.ts","handlingStrategy":"validation","validationCode":"# bash: validate deep-link schemes before building\nis_valid_scheme() {\n  local s=\"$1\"\n  [[ \"$s\" =~ ^[A-Za-z][A-Za-z0-9+.-]*$ ]] || return 1\n  case \"$s\" in http|https|file|ftp|ws|wss) return 1;; esac\n  return 0\n}\nfor scheme in \"${DEEP_LINKS[@]}\"; do\n  is_valid_scheme \"$scheme\" || { echo \"bad scheme: $s\" >&2; exit 1; }\ndone","typeGuard":"// TypeScript (validating a deno.json config before invoking the CLI)\nconst RESERVED = new Set([\"http\", \"https\", \"file\", \"ftp\", \"ws\", \"wss\"]);\nfunction isValidDeepLinkScheme(s: string): boolean {\n  return /^[A-Za-z][A-Za-z0-9+.-]*$/.test(s) && !RESERVED.has(s);\n}","tryCatchPattern":null,"preventionTips":["Pass the bare scheme (`acme`), never a URL (`acme://x`) or a trailing colon.","Use hyphens, not underscores, in scheme names.","Never register http/https/file/ftp/ws/wss as deep links.","Lint the `deep_links` array in CI with the same regex before running `deno desktop`."],"tags":["deep-link","url-scheme","validation","desktop","cli"],"backgroundTag":"url-scheme-validation","analyzedSha":"f7822238cab635a3a19f99f493f675fa81a7f9d8","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}