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
- Log/inspect the actual payload passed in and confirm it is a release object
- Fix the URL to a real releases endpoint (e.g. https://api.github.com/repos/<owner>/<repo>/releases/latest) and check owner/repo spelling
- Handle GitHub rate limits: authenticate the request or back off
- 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
- Always hit documented release endpoints and log the response body on non-200
- Authenticate GitHub API calls to avoid rate-limit error bodies reaching the parser
- Record real API responses for test fixtures instead of writing them by hand
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
- {} must be a JSON object
- auth.json 必须是 JSON 对象
- Invalid version tag: {value}
- latest.json missing version
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16).
Data as JSON: /api/errors/9f3d5ca566d376a0.
Report an issue: GitHub.