rust-lang/cargo · error · anyhow::Error

invalid url `{}`: cannot-be-a-base-URLs are not supported

Error message

invalid url `{}`: cannot-be-a-base-URLs are not supported

What it means

CanonicalUrl::new normalizes registry/git URLs for internal comparison and hashing, and it refuses URLs where url::Url::cannot_be_a_base() is true. Such URLs lack a hierarchical base (no scheme separator producing a path) — classic examples are SCP-style `host:path` strings like `github.com:rust-lang/rustfmt.git` or `git@github.com:owner/repo.git`. Cargo cannot apply its path-stripping/canonicalization logic to them, so it bails.

Source

Thrown at src/util/canonical_url.rs:25

///
/// A "canonical" url is only intended for internal comparison purposes in
/// Cargo. It's to help paper over mistakes such as depending on
/// `github.com/foo/bar` vs `github.com/foo/bar.git`. This is **only** for
/// internal purposes within Cargo and provides no means to actually read the
/// underlying string value of the `Url` it contains. This is intentional,
/// because all fetching should still happen within the context of the original
/// URL.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct CanonicalUrl(Url);

impl CanonicalUrl {
    pub fn new(url: &Url) -> CargoResult<CanonicalUrl> {
        let mut url = url.clone();

        // cannot-be-a-base-urls (e.g., `github.com:rust-lang/rustfmt.git`)
        // are not supported.
        if url.cannot_be_a_base() {
            anyhow::bail!(
                "invalid url `{}`: cannot-be-a-base-URLs are not supported",
                url
            )
        }

        // Strip a trailing slash.
        if url.path().ends_with('/') {
            url.path_segments_mut().unwrap().pop_if_empty();
        }

        // Perform further canonicalization specific to git registries, which
        // do not contain a `+` specifier.
        if !url.scheme().contains('+') {
            // For GitHub URLs specifically, just lower-case everything. GitHub
            // treats both the same, but they hash differently, and we're gonna be
            // hashing them. This wants a more general solution, and also we're
            // almost certainly not using the same case conversion rules that GitHub
            // does. (See issue #84)

View on GitHub (pinned to 0e07a15537)

Solutions

  1. Prefix the URL with a proper scheme: use `ssh://git@github.com/owner/repo.git` instead of `git@github.com:owner/repo.git`.
  2. For GitHub dependencies prefer `https://github.com/owner/repo.git`.
  3. If using a registry index URL, ensure it begins with `https://` or `sparse+https://`.
  4. Validate the URL parses with a scheme and a non-empty host before passing it to Cargo APIs.

Example fix

# Cargo.toml before
git = "git@github.com:owner/repo.git"

# after
git = "ssh://git@github.com/owner/repo.git"
# or simpler
https = "https://github.com/owner/repo.git"
Defensive patterns

Strategy: validation

Validate before calling

use url::Url;
fn is_cargo_safe_url(s: &str) -> Result<(), String> {
    let u = Url::parse(s).map_err(|e| format!("not a URL: {e}"))?;
    if u.cannot_be_a_base() {
        return Err(format!("{s} is a cannot-be-a-base URL; add a scheme like https:// or ssh://"));
    }
    Ok(())
}
// call before constructing a git dependency / registry index URL

Type guard

fn is_valid_canonical_url(s: &str) -> bool {
    Url::parse(s).map(|u| !u.cannot_be_a_base()).unwrap_or(false)
}

Try / catch

match CanonicalUrl::new(&url) {
    Ok(c) => use c,
    Err(e) if e.to_string().contains("cannot-be-a-base") => {
        return Err(anyhow!("rewrite the URL with a scheme, e.g. ssh://{url}"));
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Constructing a git dependency or registry source whose URL parses as cannot-be-a-base: passing an SCP-notation string (`host:path`) directly as a registry index URL or as a git dependency URL in Cargo.toml, or feeding such a Url into CanonicalUrl::new programmatically.

Common situations: Copying a GitHub SSH clone command's `git@github.com:owner/repo.git` argument into a Cargo.toml git dependency; using a registry index URL without an `https://`/`ssh://`/`git://` scheme; hand-building a url::Url from a bare `host:path` string.

Related errors


AI-assisted analysis of rust-lang/cargo@0e07a15537 (2026-08-06). Data as JSON: /data/errors/b879c09b0b4540cd.json. Report an issue: GitHub.