Hmbown/CodeWhale · error

provider catalog scope

Error message

provider catalog scope {key:?} exceeds bounded persistence limits

What it means

bounded_cache_for_persistence enforces per-scope persistence limits (max scopes, max rows, max bytes). When a single protected scope alone — serialized as its own cache — exceeds any of these limits, it cannot be persisted within bounds and this ensure! fails.

Solutions

  1. Reduce the number of providers/rows in the offending scope (prune stale or duplicate entries)
  2. Split the scope's contents across multiple scopes so no single scope exceeds limits
  3. Raise the persistence limits if the data is legitimately required
  4. Delete the oversized scope from the on-disk cache and let it be rebuilt incrementally
Defensive patterns

Strategy: validation

Validate before calling

let limits = persistence_limits();
let scope_rows = cached_row_count_for(scope_key);
if scope_rows > limits.max_rows {
    prune_scope(scope_key, limits.max_rows);
}

Try / catch

match bounded_cache_for_persistence(&cache, Some(scope), now, limits) {
    Err(e) if e.to_string().contains("exceeds bounded persistence limits") => {
        // evict or split the scope, then retry with a smaller cache
    }
    other => other?,
}

Prevention

When it happens

Trigger: Persisting or compacting the catalog cache when the protected scope contains more providers/rows than limits.max_rows, or its JSON envelope exceeds limits.max_bytes, or entries count exceeds min(max_scopes, MAX_CACHE_SCOPES).

Common situations: A user or a test registers an enormous number of custom catalog providers in one scope; limits were tightened by config while an old large scope remains on disk.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/4092201ac66f3525. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/provider_catalog_live.rs:794

    mut cache: ProviderCatalogCache,
    protected_scope: Option<(&str, &str)>,
    now: u64,
    limits: CachePersistenceLimits,
) -> Result<ProviderCatalogCache> {
    cache
        .entries
        .retain(|_, entry| !is_account_scoped_scope(&entry.provider, &entry.base_url_fingerprint));

    let protected_key = protected_scope
        .filter(|(provider, fingerprint)| !is_account_scoped_scope(provider, fingerprint))
        .map(|(provider, fingerprint)| ProviderCatalogCache::cache_key(provider, fingerprint));

    if let Some(key) = protected_key.as_deref()
        && let Some(entry) = cache.entries.get(key).cloned()
    {
        let mut protected_only = ProviderCatalogCache::new();
        protected_only.entries.insert(key.to_string(), entry);
        anyhow::ensure!(
            protected_only.entries.len() <= limits.max_scopes.min(MAX_CACHE_SCOPES)
                && cached_row_count(&protected_only) <= limits.max_rows
                && persisted_envelope_len(&protected_only)? <= limits.max_bytes,
            "provider catalog scope {key:?} exceeds bounded persistence limits"
        );
    }

    // Rank once while the cache/file locks are held. An older implementation
    // reserialized and rescanned the entire envelope for every eviction, which
    // made a valid sub-32-MiB file with many tiny scopes quadratic to compact.
    let mut eviction_keys = cache
        .entries
        .iter()
        .filter(|(key, _)| protected_key.as_deref() != Some(key.as_str()))
        .map(|(key, entry)| {
            let health_rank = if matches!(entry.status, CatalogStatus::Failed { .. }) {
                0u8
            } else if entry.is_stale(now) || matches!(entry.status, CatalogStatus::Stale { .. }) {

View on GitHub (pinned to 73e0f67d83)