{"record":{"id":"181281cb69081281","repo":"astrid-runtime/astrid","slug":"release-contains-too-many-assets","errorCode":null,"errorMessage":"release contains too many assets","messagePattern":"release contains too many assets","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/self_update/mod.rs","lineNumber":309,"sourceCode":"    if method.manages_own_binary() {\n        return UpdatePlan::DeferToManager {\n            manager: method.label(),\n            how: method.upgrade_command(channel),\n        };\n    }\n    UpdatePlan::ApplyInPlace\n}\n\n/// Find exactly one release asset and return its browser download URL.\npub(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\"))","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/self_update/mod.rs#L291-L327","documentation":"exact_asset_url enforces a hard cap (MAX_RELEASE_ASSETS) on the number of assets a release may contain, as a supply-chain guard so pathological or hostile releases cannot blow up matching logic. A release whose assets array exceeds the limit throws this error.","triggerScenarios":"A release fetched by tag contains more than MAX_RELEASE_ASSETS entries and any consumer calls exact_asset_url against it.","commonSituations":"Publishing pipelines accidentally attaching hundreds of artifacts to one release; a tampered or third-party release source (via ASTRID_UPDATE_REPO pointing elsewhere) with an inflated asset list.","solutions":["Inspect the release on GitHub and remove excess/stray assets so it is within the cap.","Fix the publish workflow to upload only the intended artifacts per release.","If pointing at a fork via ASTRID_UPDATE_REPO, switch back to the official repo whose releases conform.","Verify the release is authentic (expected signer/tag) — an inflated asset list can indicate tampering."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// pre-check release size before update\nconst assets = release.assets ?? [];\nif (assets.length > 50) throw new Error(\"release asset list suspiciously large\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep release publishing workflows uploading a fixed, small set of artifacts.","Only update from the official repository or trusted forks.","Treat oversized releases as a tampering signal and investigate."],"tags":["github","release","self-update","supply-chain","validation"],"backgroundTag":"value-out-of-range","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}