rust-lang/cargo · error

config.json not found

Error message

config.json not found

What it means

An HTTP (sparse) registry backend could not obtain `config.json` from the remote, and `config_opt()` returned `None`. The config is mandatory — it declares the registry's tarball URL template, auth requirements, etc. — so Cargo treats its absence as a hard error when `config()` is required.

Solutions

  1. Verify the registry URL is correct and ends with `/`: open `<index>/config.json` in a browser.
  2. Ensure auth token is set (`registries.<name>.token` or `CARGO_REGISTRIES_<NAME>_TOKEN`) if the registry requires authentication.
  3. Remove the cached index (`~/.cargo/registry/index/<name>`) and re-run `cargo fetch`.
  4. Confirm the registry server is up and serving the sparse index protocol.

Example fix

# before: missing trailing slash or wrong URL
[registries.myreg]
index = "sparse+https://myreg.example.com"

# after
[registries.myreg]
index = "sparse+https://myreg.example.com/"
token = "..."
Defensive patterns

Strategy: validation

Validate before calling

async fn ensure_config(backend: &HttpBackend<'_>) -> Result<(), anyhow::Error> {
    if backend.config_opt().await?.is_none() {
        anyhow::bail!("registry does not serve config.json; check index URL and auth");
    }
    Ok(())
}

Type guard

async fn registry_has_config(backend: &HttpBackend<'_>) -> bool {
    backend.config_opt().await.ok().flatten().is_some()
}

Try / catch

match remote.config().await {
    Ok(c) => Ok(c),
    Err(e) if e.to_string().contains("config.json not found") => {
        // surface URL/auth hint, then abort or prompt for token
        Err(anyhow!("config.json missing at {}; verify `registries.<name>.index` and token", backend.url))
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Sparse/HTTP registry `config_opt()` returns `None` — typically because the HTTP request for `config.json` returned 404, the registry is unreachable, or the cached config could not be loaded and the remote fetch yielded nothing usable. Then code that requires the config calls `config()` which converts `None` into this error.

Common situations: Pointing a sparse registry at a URL that does not serve `config.json`; typo'd `registries.<name>.index = "sparse+https://..."`; registry server outage / 404; auth required but token missing so config fetch returns empty; trailing-slash mismatch causing a 404 on `<index>/config.json`.

Related errors


AI-assisted analysis of rust-lang/cargo@eb98b54bc9 (2026-08-11). Data as JSON: /api/errors/fb7e912881c6c0ad. Report an issue: GitHub.

Appendix: source

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

        source_id: SourceId,
        gctx: &'gctx GlobalContext,
        name: &str,
    ) -> CargoResult<HttpRegistry<'gctx>> {
        Ok(HttpRegistry {
            name: name.into(),
            registry_config: Mutex::new(None),
            inner: HttpBackend::new(source_id, gctx, name)?,
        })
    }

    fn inner(&self) -> &HttpBackend<'gctx> {
        &self.inner
    }

    /// Get the registry configuration from either cache or remote.
    async fn config(&self) -> CargoResult<RegistryConfig> {
        let Some(config) = self.config_opt().await? else {
            return Err(anyhow::anyhow!("config.json not found"));
        };
        Ok(config)
    }

    /// Get the registry configuration from either cache or remote.
    /// Returns None if the config is not available.
    async fn config_opt(&self) -> CargoResult<Option<RegistryConfig>> {
        let mut config = self.registry_config.lock().await;
        if let Some(config) = &*config
            && self.inner().is_fresh(RegistryConfig::NAME)
        {
            Ok(Some(config.clone()))
        } else {
            let result = self.config_opt_inner().await?;
            *config = result.clone();
            Ok(result)
        }
    }

View on GitHub (pinned to eb98b54bc9)