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
- Verify the registry URL is correct and ends with `/`: open `<index>/config.json` in a browser.
- Ensure auth token is set (`registries.<name>.token` or `CARGO_REGISTRIES_<NAME>_TOKEN`) if the registry requires authentication.
- Remove the cached index (`~/.cargo/registry/index/<name>`) and re-run `cargo fetch`.
- 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
- Always end sparse registry URLs with `/`.
- Verify `<index>/config.json` is reachable in a browser before configuring.
- Set auth tokens for private registries.
- Clear and refetch the index if the server recovers.
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
- sparse registry url must end in a slash `/
- found patches and a path override
- local registry index path is not a directory
- local registry path is not a directory
- multiple registries are configured with the same index url
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)