{"record":{"id":"04682c97d836cb9d","repo":"astrid-runtime/astrid","slug":"release-endpoint-returned-tag-actual-tag-expe","errorCode":null,"errorMessage":"release endpoint returned tag '{actual_tag}', expected '{tag}'","messagePattern":"release endpoint returned tag '(.+?)', expected '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/update_channel.rs","lineNumber":110,"sourceCode":"    client: &reqwest::Client,\n    owner: &str,\n    repo: &str,\n    tag: &str,\n) -> anyhow::Result<serde_json::Value> {\n    let encoded_tag: String = url::form_urlencoded::byte_serialize(tag.as_bytes()).collect();\n    let url = format!(\n        \"{}/repos/{owner}/{repo}/releases/tags/{encoded_tag}\",\n        api_base()\n    );\n    let body =\n        download_bounded(client, &url, MAX_RELEASE_METADATA_BYTES, \"release metadata\").await?;\n    let json: serde_json::Value =\n        serde_json::from_slice(&body).context(\"failed to parse release metadata\")?;\n    let actual_tag = json\n        .get(\"tag_name\")\n        .and_then(|value| value.as_str())\n        .ok_or_else(|| anyhow::anyhow!(\"release has no tag_name\"))?;\n    ensure!(\n        actual_tag == tag,\n        \"release endpoint returned tag '{actual_tag}', expected '{tag}'\"\n    );\n    Ok(json)\n}\n\npub(super) async fn resolve_signed_channel(\n    client: &reqwest::Client,\n    owner: &str,\n    repo: &str,\n    channel: UpdateChannel,\n    target: &str,\n) -> anyhow::Result<ResolvedChannelRelease> {\n    // One process owns channel acceptance through the final atomic pointer\n    // commit. Without this lock, concurrent generations could interleave and\n    // leave the lower pointer as the accepted rollback floor.\n    let _lock = acquire_channel_lock(channel)?;\n    let channel_release = fetch_release_by_tag(","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/update_channel.rs#L92-L128","documentation":"`fetch_release_by_tag` downloads release metadata for a requested tag and then asserts, via `ensure!`, that the `tag_name` field in the returned JSON exactly equals the tag that was requested. This error means the release host returned metadata for a different tag than the one asked for — i.e. the HTTP layer answered but the content does not correspond to the expected release. It guards against proxies/mirrors serving stale or redirected release payloads.","triggerScenarios":"Calling `resolve_signed_channel` → `fetch_release_by_tag` against a release URL whose response body has a `tag_name` differing from the requested tag; also the closely related case where `tag_name` is missing entirely (\"release has no tag_name\").","commonSituations":"A mirror or CDN serving a cached release page for a different tag; a redirect to a generic 'latest' release; the requested tag was deleted and the endpoint fell back to another release; tampered or misconfigured update-channel URL pointing at the wrong repository.","solutions":["Re-fetch the release (clear CDN/proxy cache or bypass the mirror) and confirm the endpoint actually serves the requested tag.","Verify the update-channel configuration points at the correct release repository/owner, not a fork or 'latest' alias.","Check that the tag still exists upstream and was not deleted/recreated with a different name.","If the endpoint is supposed to redirect tag URLs, fix the URL pattern so the tag is passed through instead of being rewritten."],"exampleFix":"// before: endpoint configured with a 'latest' style URL\nrelease_url = \"https://example.com/releases/latest/download\"\n\n// after: pin the exact tag so tag_name matches the request\nrelease_url = \"https://example.com/releases/download/v1.2.3\"","handlingStrategy":"validation","validationCode":"// before resolving a channel, check the release exists under the exact tag\ncurl -fsSL \"$RELEASE_URL\" | jq -e --arg tag \"$EXPECTED_TAG\" '.tag_name == $tag'","typeGuard":"null","tryCatchPattern":"// treat fetch_release_by_tag errors as 'metadata did not match request'\nmatch resolve_signed_channel(...).await {\n    Err(e) if e.to_string().contains(\"release endpoint returned tag\") => {\n        // re-fetch without cache / fail closed; do NOT install\n    }\n    other => other?,\n}","preventionTips":["Pin exact tags in update-channel URLs; never use 'latest' aliases for signed metadata.","Bypass or purge CDN/proxy caches when validating a newly published release.","Verify the configured repository owner/name matches the official release source.","After publishing a tag, confirm the endpoint serves it before pointing clients at it."],"tags":["http","update-channel","response-validation","rust"],"backgroundTag":"unexpected-response-shape","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}