astrid-runtime/astrid · error
release endpoint returned tag '{actual_tag}', expected '{tag
Error message
release endpoint returned tag '{actual_tag}', expected '{tag}' What it means
`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.
Source
Thrown at crates/astrid-cli/src/commands/update_channel.rs:110
client: &reqwest::Client,
owner: &str,
repo: &str,
tag: &str,
) -> anyhow::Result<serde_json::Value> {
let encoded_tag: String = url::form_urlencoded::byte_serialize(tag.as_bytes()).collect();
let url = format!(
"{}/repos/{owner}/{repo}/releases/tags/{encoded_tag}",
api_base()
);
let body =
download_bounded(client, &url, MAX_RELEASE_METADATA_BYTES, "release metadata").await?;
let json: serde_json::Value =
serde_json::from_slice(&body).context("failed to parse release metadata")?;
let actual_tag = json
.get("tag_name")
.and_then(|value| value.as_str())
.ok_or_else(|| anyhow::anyhow!("release has no tag_name"))?;
ensure!(
actual_tag == tag,
"release endpoint returned tag '{actual_tag}', expected '{tag}'"
);
Ok(json)
}
pub(super) async fn resolve_signed_channel(
client: &reqwest::Client,
owner: &str,
repo: &str,
channel: UpdateChannel,
target: &str,
) -> anyhow::Result<ResolvedChannelRelease> {
// One process owns channel acceptance through the final atomic pointer
// commit. Without this lock, concurrent generations could interleave and
// leave the lower pointer as the accepted rollback floor.
let _lock = acquire_channel_lock(channel)?;
let channel_release = fetch_release_by_tag(View on GitHub (pinned to affd8760f4)
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.
Example fix
// before: endpoint configured with a 'latest' style URL release_url = "https://example.com/releases/latest/download" // after: pin the exact tag so tag_name matches the request release_url = "https://example.com/releases/download/v1.2.3"
Defensive patterns
Strategy: validation
Validate before calling
// before resolving a channel, check the release exists under the exact tag curl -fsSL "$RELEASE_URL" | jq -e --arg tag "$EXPECTED_TAG" '.tag_name == $tag'
Type guard
null
Try / catch
// treat fetch_release_by_tag errors as 'metadata did not match request'
match resolve_signed_channel(...).await {
Err(e) if e.to_string().contains("release endpoint returned tag") => {
// re-fetch without cache / fail closed; do NOT install
}
other => other?,
} Prevention
- 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.
When it happens
Trigger: 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").
Common situations: 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.
Related errors
- failed to fetch {name} from {url} (HTTP {})
- {label} download failed: HTTP {status}
- signed channel has no target '{triple}'
- signed channel version '{value}' is not canonical semver
- {label} target set is invalid
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/04682c97d836cb9d.
Report an issue: GitHub.