denoland/deno · error · AnyError

Registry API URL cannot be used as a base URL

Error message

Registry API URL cannot be used as a base URL

What it means

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.

Source

Thrown at cli/registry.rs:193

      package,
      "versions",
      version,
      "provenance",
    ],
  )
}

fn append_path_segments(
  base_url: &Url,
  segments: &[&str],
) -> Result<Url, AnyError> {
  let mut url = base_url.clone();
  url.set_query(None);
  url.set_fragment(None);
  url
    .path_segments_mut()
    .map_err(|_| {
      deno_core::anyhow::anyhow!(
        "Registry API URL cannot be used as a base URL"
      )
    })?
    .pop_if_empty()
    .extend(segments);
  Ok(url)
}

pub async fn get_package(
  client: &HttpClient,
  registry_api_url: &Url,
  scope: &str,
  package: &str,
  authorization: Option<&str>,
) -> Result<http::Response<deno_fetch::ResBody>, AnyError> {
  let package_url = get_package_api_url(registry_api_url, scope, package)?;
  let mut request = client.get(package_url)?;
  // The registry responds with a 404 for private packages unless the request

View on GitHub (pinned to f7822238ca)

Solutions

  1. 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`.
  2. Check deno.json / deno.jsonc `scopes`/registry endpoint configuration and any DENO_* env vars for a malformed or non-HTTP(S) URL value.
  3. If constructing the URL programmatically (tests, tooling), use `Url::parse("https://...")` rather than hand-built or `data:` URLs.
  4. If behind a custom gateway, ensure it preserves a normal path structure (`https://host/base/`) so path segments can be appended.

Example fix

// before (deno.json - cannot-be-a-base URL)
{
  "scopes": {
    "jsr": { "endpoints": { "api": "data:internal-registry" } }
  }
}

// after
{
  "scopes": {
    "jsr": { "endpoints": { "api": "https://registry.internal.example.com/api" } }
  }
}
Defensive patterns

Strategy: validation

Validate before calling

use url::Url;

fn validate_registry_api_url(raw: &str) -> Result<Url, String> {
  let url = Url::parse(raw).map_err(|e| format!("invalid URL: {e}"))?;
  if !matches!(url.scheme(), "http" | "https") {
    return Err(format!("registry API URL must be http(s), got scheme '{}'", url.scheme()));
  }
  if url.cannot_be_a_base() {
    return Err("registry API URL cannot be a base URL (no hierarchical path)".into());
  }
  if url.host_str().is_none() {
    return Err("registry API URL must include a host".into());
  }
  Ok(url)
}

Type guard

fn is_valid_base_url(url: &Url) -> bool {
  !url.cannot_be_a_base()
    && matches!(url.scheme(), "http" | "https")
    && url.host_str().is_some()
}

Try / catch

match validate_registry_api_url(&raw_url) {
  Ok(url) => registry::get_package(&client, &url, scope, package, auth).await,
  Err(e) => {
    eprintln!("Cannot talk to JSR registry: {e}");
    std::process::exit(1);
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of denoland/deno@f7822238ca (2026-08-29). Data as JSON: /api/errors/0e04a0aed2671d51. Report an issue: GitHub.