BigPizzaV3/CodexPlusPlus · error · anyhow::Error

release payload missing tag_name

Error message

release payload missing tag_name

What it means

release_from_github_payload maps a GitHub releases API object into a Release. The first required field is tag_name, read via payload.get("tag_name").and_then(Value::as_str); when it is absent (or not a string) the function bails with "release payload missing tag_name" before examining anything else. So any parseable JSON body without a top-level string tag_name triggers it.

Source

Thrown at crates/codex-plus-core/src/update.rs:77

        .split('.')
        .map(|part| part.parse::<u64>().map_err(Into::into))
        .collect()
}

pub fn is_newer_version(candidate: &str, current: &str) -> anyhow::Result<bool> {
    let mut left = parse_version_tag(candidate)?;
    let mut right = parse_version_tag(current)?;
    let len = left.len().max(right.len());
    left.resize(len, 0);
    right.resize(len, 0);
    Ok(left > right)
}

pub fn release_from_github_payload(payload: &Value) -> anyhow::Result<Release> {
    let version = payload
        .get("tag_name")
        .and_then(Value::as_str)
        .ok_or_else(|| anyhow::anyhow!("release payload missing tag_name"))?
        .to_string();
    let assets = payload
        .get("assets")
        .and_then(Value::as_array)
        .into_iter()
        .flatten()
        .filter_map(|asset| {
            Some((
                asset.get("name")?.as_str()?.to_string(),
                asset.get("browser_download_url")?.as_str()?.to_string(),
            ))
        })
        .collect::<Vec<_>>();
    let selected = select_update_asset(&assets);
    Ok(Release {
        version,
        url: payload
            .get("html_url")

View on GitHub (pinned to 1f431ae49b)

Solutions

  1. Log/inspect the actual payload passed in and confirm it is a release object
  2. Fix the URL to a real releases endpoint (e.g. https://api.github.com/repos/<owner>/<repo>/releases/latest) and check owner/repo spelling
  3. Handle GitHub rate limits: authenticate the request or back off
  4. In tests, add "tag_name": "v0.1.0" to the fixture

Example fix

// before
let release = release_from_github_payload(&payload)?;

// after
let release = release_from_github_payload(&payload).map_err(|err| {
    let keys = payload.as_object().map(|m| m.keys().collect::<Vec<_>>());
    anyhow::anyhow!("{err} (payload keys: {keys:?})")
})?;
Defensive patterns

Strategy: type-guard

Validate before calling

if !is_github_release_payload(&payload) {
    anyhow::bail!("not a GitHub release payload: {payload}");
}
let release = release_from_github_payload(&payload)?;

Type guard

fn is_github_release_payload(payload: &serde_json::Value) -> bool {
    payload
        .get("tag_name")
        .and_then(serde_json::Value::as_str)
        .is_some_and(|tag| !tag.trim().is_empty())
}

Prevention

When it happens

Trigger: Feeding release_from_github_payload JSON that is not a release object: a GitHub error body like {"message":"Not Found", ...} from a wrong owner/repo or wrong endpoint, a rate-limit response, or a test fixture that copied only the assets array.

Common situations: Wrong repo owner/name in the API URL; querying an endpoint that returns an error object instead of a release; GitHub unauthenticated rate limiting (60 req/h); test fixtures written from memory instead of recorded responses.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16). Data as JSON: /api/errors/9f3d5ca566d376a0. Report an issue: GitHub.