{"record":{"id":"44be94a956be4a65","repo":"langchain-ai/deepagents","slug":"refused-pricing-catalog-with-fetched-count-provi","errorCode":null,"errorMessage":"Refused pricing catalog with {fetched_count} providers ({bundled_count} bundled)","messagePattern":"Refused pricing catalog with (.+?) providers \\((.+?) bundled\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/cost_tracking.py","lineNumber":473,"sourceCode":"                _TRUNCATED_CATALOG_REPORTED = True\n                logger.warning(\n                    \"Refusing an upstream pricing catalog listing %d providers \"\n                    \"against %d bundled with the installed package; continuing \"\n                    \"with the catalog already in use. Upstream data.json may be \"\n                    \"mid-publish.\",\n                    fetched_count,\n                    bundled_count,\n                )\n            # Raising rather than returning `None` keeps the last good catalog:\n            # `_update_prices` installs whatever `fetch` returns, `None`\n            # included, so returning would discard a healthy earlier fetch. The\n            # background loop treats a raise as a failed refresh and retries on\n            # the next interval.\n            msg = (\n                f\"Refused pricing catalog with {fetched_count} providers \"\n                f\"({bundled_count} bundled)\"\n            )\n            raise ValueError(msg)\n\n    return _GuardedUpdatePrices()\n\n\ndef _prices_auto_update_enabled() -> bool:\n    \"\"\"Resolve the `update.prices_auto_update` option through the manifest.\n\n    Routing the gate through the shared resolver keeps env-over-TOML precedence\n    and the `config get update.prices_auto_update` report in lockstep with what\n    the updater actually does; reading the env var inline would show a user who\n    opted out in `config.toml` `false` while the hourly fetch still started.\n\n    Returns:\n        `True` unless the option resolved to disabled or its manifest entry is\n            missing.\n    \"\"\"\n    from deepagents_code.config_manifest import _emit_ranked_diagnostics, get_option\n    from deepagents_code.configuration.resolver import get_config_resolver","sourceCodeStart":455,"sourceCodeEnd":491,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/cost_tracking.py#L455-L491","documentation":"The pricing-catalog fetcher refuses to replace the in-memory pricing table when a freshly fetched catalog looks implausibly small — containing no providers at all or only the bundled fallback set. This guards against a corrupted upstream catalog, breaking API change, or hijacked/mis-serving endpoint silently wiping real pricing data. The raise happens inside a guarded update, so the existing catalog stays intact and the background refresh loop treats it as a failed refresh and retries on the next interval.","triggerScenarios":"Calling `fetch()` on the guarded pricing updater (directly or via the background auto-update loop) when the fetched catalog parses to a provider count at or below the bundled-provider count — typically 0 providers parsed from the response.","commonSituations":"Upstream pricing API returns an empty or malformed payload (HTML error page, truncated JSON, renamed schema field yielding zero parsed providers); a proxy or captive portal serves a stub response; SDK version expects a newer catalog schema than the server serves.","solutions":["Inspect the raw response from the pricing-catalog endpoint and confirm it contains per-provider pricing entries.","Verify the catalog URL/endpoint has not been redirected or stubbed by a proxy, VPN, or captive portal.","Check whether the installed library version expects a newer catalog schema than upstream serves; upgrade or pin accordingly.","Re-run after transient network issues resolve — the background loop retries automatically, so no code change is needed."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"catalog = fetch_catalog_raw()  # inspect upstream before accepting\nif len(catalog.providers) <= BUNDLED_PROVIDER_COUNT:\n    skip_refresh()  # keep existing pricing table","typeGuard":"def catalog_looks_valid(catalog, bundled_count: int) -> bool:\n    return catalog is not None and len(catalog.providers) > bundled_count","tryCatchPattern":"try:\n    updater.fetch()\nexcept ValueError as exc:\n    logger.warning(\"Pricing refresh rejected: %s — keeping existing catalog\", exc)","preventionTips":["Monitor the upstream pricing endpoint for schema or availability changes.","Pin/verify the catalog URL in configuration to avoid proxy stubs.","Alert on refresh failures so a stale catalog is noticed.","Keep the library updated to match the current upstream catalog schema."],"tags":["pricing","data-validation","network","safety-guard"],"backgroundTag":"empty-or-invalid-upstream-response","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}