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

  1. Configure at least one ad-list URL in the settings that feeds fetch_ad_list
  2. Verify the URL list is not accidentally emptied by config load/merge logic
  3. If the real network error is expected, ensure the loop records `last_error` (it does) and check logs for the per-URL error
  4. 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

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)