BigPizzaV3/CodexPlusPlus · error
ad list unavailable
Error message
ad list unavailable
What it means
Sentinel error returned by `fetch_ad_list_from_urls` when none of the configured ad-list URLs produced a payload. The function iterates the URLs, storing each failure in `last_error`, and if the URL list is empty (or no URL was attempted at all) there is no `last_error` to propagate, so this generic fallback is raised instead. Callers `fetch_ad_list` and the backup-URL retry helper surface it as 'ad list unavailable'.
Solutions
- Configure at least one ad-list URL in the settings that feeds fetch_ad_list
- Verify the URL list is not accidentally emptied by config load/merge logic
- If the real network error is expected, ensure the loop records `last_error` (it does) and check logs for the per-URL error
- Improve the fallback to include the URL list state for debuggability
Example fix
// before
Err(last_error.unwrap_or_else(|| anyhow::anyhow!("ad list unavailable")))
// after
Err(last_error.unwrap_or_else(|| {
anyhow::anyhow!("ad list unavailable: no ad list URLs configured (configured=0)")
})) Defensive patterns
Strategy: validation
Validate before calling
if ad_list_urls.is_empty() {
return Err(anyhow!("no ad list URLs configured; add at least one URL"));
} Try / catch
match fetch_ad_list().await {
Err(e) if e.to_string() == "ad list unavailable" => configure_default_ad_urls(),
Err(e) => return Err(e),
Ok(list) => apply(list),
} Prevention
- Validate that the ad URL list is non-empty at settings load time
- Seed sensible default URLs on first run
- Log per-URL fetch errors so the sentinel is never the only clue
When it happens
Trigger: Calling `fetch_ad_list`/`fetch_ad_list_tries_backup_url_when_primary_fails` when the configured ad URL list is empty, so the loop body never runs and `last_error` stays None; each attempted URL's real error is stored but this message is returned only when no URL was tried.
Common situations: Fresh install with no ad-list URLs configured in settings, config migration wiped the URL list, or a code path passes an empty URL slice.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/846f3ac92eddf9c9.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/ads.rs:166
let cache_bust = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_millis())
.unwrap_or_default();
let mut last_error = None;
for url in urls {
let url = cache_busted_ad_url(url.as_ref(), cache_bust);
let result = async {
let response = client.get(url).send().await?.error_for_status()?;
let payload = response.json::<Value>().await?;
Ok::<_, anyhow::Error>(normalize_ad_payload(payload))
}
.await;
match result {
Ok(payload) => return Ok(payload),
Err(error) => last_error = Some(error),
}
}
Err(last_error.unwrap_or_else(|| anyhow::anyhow!("ad list unavailable")))
}
#[cfg(test)]
mod tests {
use super::*;
fn sponsored(id: &str) -> Value {
json!({
"id": id,
"type": "sponsor",
"title": id,
"description": "描述",
"url": "https://example.com/",
})
}
#[test]
fn top_ad_is_kept_out_of_the_recommendation_pool() {View on GitHub (pinned to b1ed92e5e4)