{"record":{"id":"0e04a0aed2671d51","repo":"denoland/deno","slug":"registry-api-url-cannot-be-used-as-a-base-url","errorCode":null,"errorMessage":"Registry API URL cannot be used as a base URL","messagePattern":"Registry API URL cannot be used as a base URL","errorType":"exception","errorClass":"AnyError","httpStatus":null,"severity":"error","filePath":"cli/registry.rs","lineNumber":193,"sourceCode":"      package,\n      \"versions\",\n      version,\n      \"provenance\",\n    ],\n  )\n}\n\nfn append_path_segments(\n  base_url: &Url,\n  segments: &[&str],\n) -> Result<Url, AnyError> {\n  let mut url = base_url.clone();\n  url.set_query(None);\n  url.set_fragment(None);\n  url\n    .path_segments_mut()\n    .map_err(|_| {\n      deno_core::anyhow::anyhow!(\n        \"Registry API URL cannot be used as a base URL\"\n      )\n    })?\n    .pop_if_empty()\n    .extend(segments);\n  Ok(url)\n}\n\npub async fn get_package(\n  client: &HttpClient,\n  registry_api_url: &Url,\n  scope: &str,\n  package: &str,\n  authorization: Option<&str>,\n) -> Result<http::Response<deno_fetch::ResBody>, AnyError> {\n  let package_url = get_package_api_url(registry_api_url, scope, package)?;\n  let mut request = client.get(package_url)?;\n  // The registry responds with a 404 for private packages unless the request","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/denoland/deno/blob/f7822238cab635a3a19f99f493f675fa81a7f9d8/cli/registry.rs#L175-L211","documentation":"Denos registry client builds JSR API endpoint URLs by appending path segments (scopes/packages/versions/...) to a configured registry API base URL via `url.path_segments_mut()` in append_path_segments (cli/registry.rs:183). The `url` crate returns Err from path_segments_mut for URLs that cannot have path segments — notably URLs whose path is not slash-based, such as `cannot-be-a-base` URLs (e.g. `data:`, `mailto:`, or a custom scheme without an authority/path). When that happens the library aborts with this error instead of building a malformed request URL.","triggerScenarios":"Calling get_package, get_package_version, or get_package_version_provenance (which all route through append_path_segments) with a registry_api_url that is a cannot-be-a-base URL or otherwise has no mutable path segments — e.g. a deno config `registry`/scope import map entry or DENO_REGISTRY_URL override pointing at something like `data:...`, `mailto:...`, or a custom scheme URL without a standard `scheme://host/path` form. Also triggered if the URL somehow lost its authority so the path cannot be segmented.","commonSituations":"A misconfigured publish/registry URL in deno.json (scope `endpoints` or custom registry override), a corrupted environment variable used as the registry API URL, copying a non-HTTP URL into the registry config, or a test/mock injecting a placeholder URL like `data:test` instead of a real `https://` base.","solutions":["Fix the configured registry API URL to a normal hierarchical URL with a scheme, host and path, e.g. `https://jsr.io` (default) or your proxy's `https://registry.example.com/api`.","Check deno.json / deno.jsonc `scopes`/registry endpoint configuration and any DENO_* env vars for a malformed or non-HTTP(S) URL value.","If constructing the URL programmatically (tests, tooling), use `Url::parse(\"https://...\")` rather than hand-built or `data:` URLs.","If behind a custom gateway, ensure it preserves a normal path structure (`https://host/base/`) so path segments can be appended."],"exampleFix":"// before (deno.json - cannot-be-a-base URL)\n{\n  \"scopes\": {\n    \"jsr\": { \"endpoints\": { \"api\": \"data:internal-registry\" } }\n  }\n}\n\n// after\n{\n  \"scopes\": {\n    \"jsr\": { \"endpoints\": { \"api\": \"https://registry.internal.example.com/api\" } }\n  }\n}","handlingStrategy":"validation","validationCode":"use url::Url;\n\nfn validate_registry_api_url(raw: &str) -> Result<Url, String> {\n  let url = Url::parse(raw).map_err(|e| format!(\"invalid URL: {e}\"))?;\n  if !matches!(url.scheme(), \"http\" | \"https\") {\n    return Err(format!(\"registry API URL must be http(s), got scheme '{}'\", url.scheme()));\n  }\n  if url.cannot_be_a_base() {\n    return Err(\"registry API URL cannot be a base URL (no hierarchical path)\".into());\n  }\n  if url.host_str().is_none() {\n    return Err(\"registry API URL must include a host\".into());\n  }\n  Ok(url)\n}","typeGuard":"fn is_valid_base_url(url: &Url) -> bool {\n  !url.cannot_be_a_base()\n    && matches!(url.scheme(), \"http\" | \"https\")\n    && url.host_str().is_some()\n}","tryCatchPattern":"match validate_registry_api_url(&raw_url) {\n  Ok(url) => registry::get_package(&client, &url, scope, package, auth).await,\n  Err(e) => {\n    eprintln!(\"Cannot talk to JSR registry: {e}\");\n    std::process::exit(1);\n  }\n}","preventionTips":["Always use an absolute http(s) URL (e.g. https://jsr.io) for the registry API endpoint in deno.json or env.","Never paste non-hierarchical URLs (data:, mailto:, bare hostnames) into registry configuration.","Validate the URL parses with Url::parse and has a host before storing it in config.","When mocking in tests, use `Url::parse(\"https://jsr.test/\")` rather than placeholder schemes."],"tags":["url","configuration","registry","deno"],"backgroundTag":"invalid-base-url","analyzedSha":"f7822238cab635a3a19f99f493f675fa81a7f9d8","analyzedAt":"2026-08-29T08:55:39.519Z","contentChangedAt":"2026-08-29T08:55:39.519Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}