{"record":{"id":"846f3ac92eddf9c9","repo":"BigPizzaV3/CodexPlusPlus","slug":"ad-list-unavailable","errorCode":null,"errorMessage":"ad list unavailable","messagePattern":"ad list unavailable","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/codex-plus-core/src/ads.rs","lineNumber":229,"sourceCode":"    let cache_bust = SystemTime::now()\n        .duration_since(UNIX_EPOCH)\n        .map(|duration| duration.as_millis())\n        .unwrap_or_default();\n    let mut last_error = None;\n    for url in urls {\n        let url = cache_busted_ad_url(url.as_ref(), cache_bust);\n        let result = async {\n            let response = client.get(url).send().await?.error_for_status()?;\n            let payload = response.json::<Value>().await?;\n            Ok::<_, anyhow::Error>(normalize_ad_payload(payload))\n        }\n        .await;\n        match result {\n            Ok(payload) => return Ok(payload),\n            Err(error) => last_error = Some(error),\n        }\n    }\n    Err(last_error.unwrap_or_else(|| anyhow::anyhow!(\"ad list unavailable\")))\n}\n","sourceCodeStart":211,"sourceCodeEnd":231,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/1f431ae49b57b3055e0e6845ba6156c6b4232b4d/crates/codex-plus-core/src/ads.rs#L211-L231","documentation":"fetch_ad_list_from_urls() (crates/codex-plus-core/src/ads.rs:229) iterates the candidate URL list, keeping the last per-URL error in last_error; after the loop it returns Err(last_error.unwrap_or_else(|| anyhow!(\"ad list unavailable\"))). The unwrap_or_else branch only executes when the loop body never ran, i.e. the urls slice is empty — with a non-empty list a real network/HTTP/JSON error is propagated instead. So this exact message means zero ad-list URLs were attempted, not 'network down'.","triggerScenarios":"Calling fetch_ad_list_from_urls() with an empty &[] (or an empty configured URL list); the public fetch_ad_list() always passes DEFAULT_AD_LIST_URLS (2 entries) and can never produce this message on its own.","commonSituations":"A settings/config layer that lets users override the ad-list URL array is saved as an empty array; tests pass an empty Vec; code that filters URLs (e.g. removing invalid entries) accidentally filters everything out before calling.","solutions":["Pass at least one ad-list URL — the simplest fix is to fall back to ads::DEFAULT_AD_LIST_URLS when the configured list is empty","Audit the caller that builds the URL slice: an over-aggressive filter or an empty default from settings is the usual culprit","If you instead saw a network-ish error, you are not hitting this branch — the propagated last_error names the real cause (DNS, proxy, HTTP status, JSON decode)"],"exampleFix":"// before: empty list reaches the fetcher and yields the generic error\nlet urls: Vec<String> = settings.ad_list_urls.unwrap_or_default();\nlet list = fetch_ad_list_from_urls(&urls).await?;\n\n// after: fall back to defaults when the configured list is empty\nlet urls: Vec<String> = settings.ad_list_urls.filter(|u| !u.is_empty());\nlet urls: Vec<String> = if urls.is_empty() {\n    DEFAULT_AD_LIST_URLS.iter().map(|s| s.to_string()).collect()\n} else { urls };\nlet list = fetch_ad_list_from_urls(&urls).await?;","handlingStrategy":"validation","validationCode":"// Before fetching, guarantee a non-empty candidate list\nlet urls: Vec<String> = configured_urls.into_iter().filter(|u| !u.trim().is_empty()).collect();\nlet urls = if urls.is_empty() {\n    DEFAULT_AD_LIST_URLS.iter().map(|s| s.to_string()).collect()\n} else { urls };\nassert!(!urls.is_empty(), \"ad list needs at least one URL\");\nlet list = fetch_ad_list_from_urls(&urls).await?;","typeGuard":"fn has_ad_urls(urls: &[String]) -> bool { !urls.is_empty() }","tryCatchPattern":"// The real network failure surfaces as the propagated last_error, not this message;\n// match on it only to detect the empty-list config bug:\nmatch fetch_ad_list_from_urls(&urls).await {\n    Ok(payload) => Ok(payload),\n    Err(e) if e.to_string() == \"ad list unavailable\" => {\n        // config bug: zero URLs were attempted — fix the source list\n        Err(anyhow!(\"ad list URL list is empty; check settings\"))\n    }\n    Err(e) => Err(e), // real per-URL network/HTTP error is embedded here\n}","preventionTips":["Default empty ad-list settings to DEFAULT_AD_LIST_URLS","Never pass a filtered URL list without re-checking emptiness","Treat 'ad list unavailable' as a configuration assertion failure, distinct from network errors"],"tags":["ads","configuration","empty-list","network"],"backgroundTag":"empty-url-list","analyzedSha":"1f431ae49b57b3055e0e6845ba6156c6b4232b4d","analyzedAt":"2026-08-16T20:54:18.598Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}