rust-lang/cargo · error

registry said cache valid when no cache exists

Error message

registry said cache valid when no cache exists

What it means

The registry backend returned `LoadResponse::CacheValid` (meaning the client's cached `index_version` is still current), but the caller had no in-memory `cached_summaries` to return. This is an internal invariant violation: the backend should not claim a cache is valid when the index layer holds nothing to validate against.

Solutions

  1. Clear the entire registry index cache: `rm -rf ~/.cargo/registry/index/<name>` then `cargo fetch`.
  2. If operating a private registry, ensure it returns `404`/data (not `304 Not Modified`) when the client supplies an `index_version` it never received data for.
  3. Update Cargo — this often indicates a bug fixed in newer versions.
  4. Report with `--verbose` logs if it reproduces on the latest Cargo.

Example fix

# before: desync between version tracker and cache body
$ cargo build
error: registry said cache valid when no cache exists

# after: full index reset
$ rm -rf ~/.cargo/registry/index/<name>
$ cargo fetch
Defensive patterns

Strategy: try-catch

Validate before calling

// Caller-side: only pass index_version when we actually have cached data.
let version_to_send = cached_summaries.as_ref().and_then(|_| index_version.as_deref());
let resp = load.load(root, relative.as_ref(), version_to_send).await?;

Type guard

fn cache_state_consistent(cached: &Option<Summaries>, resp: &LoadResponse) -> bool {
    !matches!(resp, LoadResponse::CacheValid) || cached.is_some()
}

Try / catch

match response {
    LoadResponse::CacheValid if cached_summaries.is_none() => {
        // recover: invalidate and re-load unconditionally
        cache_manager.invalidate(lowered_name);
        self.load_summaries(name, load).await
    }
    other => /* normal handling */,
};

Prevention

When it happens

Trigger: In `load_summaries`, after `cache_manager.get(lowered_name)` failed to parse (or returned nothing) so `cached_summaries` is `None`, yet the remote responded with `CacheValid`. Indicates a logic bug, a cache/index state desync, or a registry server incorrectly returning 304/Not-Modified for content the client never had.

Common situations: Corrupt local cache that failed to parse (so `cached_summaries` is None) combined with a server returning a cache-valid signal; race between cache invalidation and load; bug in a custom registry backend; partial cache after a manual `rm` of cache files but not the version tracker.

Related errors


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

Appendix: source

Thrown at src/sources/registry/index/mod.rs:556

                Ok((s, v)) => {
                    cached_summaries = Some(s);
                    index_version = Some(v);
                }
                Err(e) => {
                    tracing::debug!("failed to parse {lowered_name:?} cache: {e}");
                }
            }
        }

        let response = load
            .load(root, relative.as_ref(), index_version.as_deref())
            .await?;

        match response {
            LoadResponse::CacheValid => {
                tracing::debug!("fast path for registry cache of {:?}", relative);
                if cached_summaries.is_none() {
                    return Err(anyhow::anyhow!(
                        "registry said cache valid when no cache exists"
                    ));
                }
                return Ok(cached_summaries);
            }
            LoadResponse::NotFound => {
                cache_manager.invalidate(lowered_name);
                return Ok(None);
            }
            LoadResponse::Data {
                raw_data,
                index_version,
            } => {
                // This is the fallback path where we actually talk to the registry backend to load
                // information. Here we parse every single line in the index (as we need
                // to find the versions)
                tracing::debug!("slow path for {:?}", relative);
                let mut cache = SummariesCache::default();

View on GitHub (pinned to 98a09e7e7d)