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

sparse registry url must end in a slash `/`: {url}

Error message

sparse registry url must end in a slash `/`: {url}

What it means

When constructing an `HttpBackend` for a sparse registry (src/sources/registry/http_remote.rs:390), Cargo requires the index URL to end with `/` because it builds crate index URLs by string concatenation (`url + relative_path`). A URL without a trailing slash would silently produce wrong paths, so it bails hard.

Source

Thrown at src/sources/registry/http_remote.rs:393

    login_url: RefCell<Option<Url>>,

    /// Headers received with an HTTP 401.
    auth_error_headers: RefCell<Vec<String>>,

    /// Disables status messages.
    quiet: Cell<bool>,
}

impl<'gctx> HttpBackend<'gctx> {
    pub fn new(
        source_id: SourceId,
        gctx: &'gctx GlobalContext,
        name: &str,
    ) -> CargoResult<HttpBackend<'gctx>> {
        let url = source_id.url().as_str();
        // Ensure the url ends with a slash so we can concatenate paths.
        if !url.ends_with('/') {
            anyhow::bail!("sparse registry url must end in a slash `/`: {url}")
        }
        assert!(source_id.is_sparse());
        let url = url
            .strip_prefix("sparse+")
            .expect("sparse registry needs sparse+ prefix")
            .into_url()
            .expect("a url with the sparse+ stripped should still be valid");

        let index_cache_path = gctx.registry_index_path().join(name);
        Ok(HttpBackend {
            index_cache_path: index_cache_path.clone(),
            crate_cache_path: gctx.registry_cache_path().join(name),
            source_id,
            gctx,
            url,
            progress: RefCell::new(Some(Progress::with_style(
                "Fetch",
                ProgressStyle::Indeterminate,

View on GitHub (pinned to 0e07a15537)

Solutions

  1. Add a trailing `/` to the index URL everywhere it's configured.
  2. Search all config sources (`.cargo/config.toml`, `Cargo.toml`, `~/.cargo/config.toml`, `CARGO_REGISTRIES_<NAME>_INDEX` env) for the offending URL.

Example fix

# before
[registries.my-reg]
index = "sparse+https://index.crates.io"   # -> sparse registry url must end in a slash

# after
[registries.my-reg]
index = "sparse+https://index.crates.io/"
Defensive patterns

Strategy: validation

Validate before calling

// Validate the trailing slash when constructing a sparse registry SourceId.
fn ensure_trailing_slash(url: &str) -> CargoResult<()> {
    if !url.ends_with('/') {
        anyhow::bail!("sparse registry url must end in a slash `/`: {url}");
    }
    Ok(())
}

Type guard

pub fn is_well_formed_sparse_url(url: &str) -> bool {
    url.starts_with("sparse+") && url.ends_with('/')
}

Prevention

When it happens

Trigger: Configuring a registry with `index = "sparse+https://example.com/index"` (no trailing slash); passing a bare URL to `cargo --index sparse+https://...`. Triggered at `HttpBackend::new` during source creation.

Common situations: Copy-pasting a sparse URL without the trailing slash in `.cargo/config.toml`, `Cargo.toml` `[registries]`, or a `--index` CLI argument; migrating from the git protocol (which doesn't require a trailing slash) to sparse.

Related errors


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