{"record":{"id":"85e7886a36498ede","repo":"astrid-runtime/astrid","slug":"release-contains-duplicate-asset-name","errorCode":null,"errorMessage":"release contains duplicate asset '{name}'","messagePattern":"release contains duplicate asset '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/self_update/mod.rs","lineNumber":319,"sourceCode":"pub(super) fn exact_asset_url<'a>(\n    release: &'a serde_json::Value,\n    name: &str,\n) -> anyhow::Result<&'a str> {\n    let assets = release\n        .get(\"assets\")\n        .and_then(serde_json::Value::as_array)\n        .ok_or_else(|| anyhow::anyhow!(\"release has no asset list\"))?;\n    anyhow::ensure!(\n        assets.len() <= MAX_RELEASE_ASSETS,\n        \"release contains too many assets\"\n    );\n    let mut matches = assets\n        .iter()\n        .filter(|asset| asset.get(\"name\").and_then(|value| value.as_str()) == Some(name));\n    let asset = matches\n        .next()\n        .ok_or_else(|| anyhow::anyhow!(\"release has no asset '{name}'\"))?;\n    anyhow::ensure!(\n        matches.next().is_none(),\n        \"release contains duplicate asset '{name}'\"\n    );\n    asset\n        .get(\"browser_download_url\")\n        .and_then(|value| value.as_str())\n        .filter(|url| !url.is_empty())\n        .ok_or_else(|| anyhow::anyhow!(\"release asset '{name}' has no download URL\"))\n}\n\nfn publisher_bundle_url<'a>(\n    release: &'a serde_json::Value,\n    archive_name: &str,\n) -> Result<&'a str, UpdateStageError> {\n    let bundle_name = format!(\"{archive_name}.sigstore.json\");\n    exact_asset_url(release, &bundle_name)\n        .map_err(|error| UpdateStageError::publisher(error.to_string()))\n}","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/self_update/mod.rs#L301-L337","documentation":"exact_asset_url requires asset names within a release to be unique. If two or more assets share the requested name it throws this error, refusing to guess which one to download. This is another supply-chain integrity check on release contents.","triggerScenarios":"A release contains duplicate-named assets (GitHub normally forbids this, but forks/mirrors or API-forged JSON can have it) and exact_asset_url matches more than one entry for the requested name.","commonSituations":"Using a fork or custom update source whose tooling re-uploaded assets without deleting originals; a compromised or manipulated release payload.","solutions":["Inspect the release and delete the duplicate asset so only one remains.","Fix the publishing workflow to delete-then-upload assets, preventing duplicates.","Switch ASTRID_UPDATE_REPO back to the official repository, which enforces unique asset names.","Treat duplicates as a red flag: verify release authenticity before trusting it."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// detect duplicate asset names in a release before updating\nconst names = release.assets.map(a => a.name);\nif (new Set(names).size !== names.length) throw new Error(\"duplicate asset names in release\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use delete-then-upload in publish workflows to prevent duplicate assets.","Only update from the official repo; GitHub itself enforces uniqueness there.","Investigate duplicates as a potential integrity compromise."],"tags":["github","release","self-update","supply-chain","integrity"],"backgroundTag":"internal-invariant-violation","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"}