{"record":{"id":"54720704239a04de","repo":"tonhowtf/omniget","slug":"veio-sem-digest-da-api-do-github-download-descartado-547207","errorCode":null,"errorMessage":"{} veio sem digest da API do GitHub; download descartado","messagePattern":"(.+?) veio sem digest da API do GitHub; download descartado","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/github.rs","lineNumber":104,"sourceCode":"    asset: &ReleaseAsset,\n    allow_unverified: bool,\n    progress: &super::ProgressFn,\n    id: &str,\n) -> anyhow::Result<Vec<u8>> {\n    let tmp = super::temp_dir().join(format!(\"{}.download\", asset.name));\n    super::download_to(client, &asset.url, &tmp, progress, id).await?;\n    let bytes = tokio::fs::read(&tmp).await?;\n    let _ = tokio::fs::remove_file(&tmp).await;\n    match asset.digest.as_deref() {\n        Some(expected) => integrity::verify_sha256(&bytes, expected, &asset.name)?,\n        None if allow_unverified => {\n            tracing::warn!(\n                \"[tools] {} veio sem digest; aceito sem verificacao\",\n                asset.name\n            )\n        }\n        None => {\n            return Err(anyhow!(\n                \"{} veio sem digest da API do GitHub; download descartado\",\n                asset.name\n            ))\n        }\n    }\n    Ok(bytes)\n}\n\npub fn unpack(data: &[u8], name: &str, dest: &Path) -> anyhow::Result<()> {\n    std::fs::create_dir_all(dest)?;\n    if name.ends_with(\".zip\") {\n        let mut archive = zip::ZipArchive::new(std::io::Cursor::new(data))\n            .map_err(|e| anyhow!(\"zip invalido: {}\", e))?;\n        for i in 0..archive.len() {\n            let mut file = archive.by_index(i)?;\n            let Some(rel) = file.enclosed_name() else {\n                continue;\n            };","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/github.rs#L86-L122","documentation":"github::download() verifies integrity using the digest returned by the GitHub API. If the asset carries no digest (None) the function refuses to return unverified bytes and errors instead — except in the branch above where a warning permits accepting without verification; this hard-fail branch is for when no digest is available at all and verification is mandatory.","triggerScenarios":"Calling download() with a ReleaseAsset whose digest field is None because the GitHub API response lacked 'digest' for that asset — older API payloads, cached/proxied responses, or an asset constructed manually without a digest.","commonSituations":"Older GitHub API responses predating the digest field; manually assembled ReleaseAsset structs in tests/tools; third-party mirrors or API caches stripping the field.","solutions":["Re-fetch the asset info from the live GitHub API so the 'digest' field is populated","If verification is intentionally not required, use the path that warns and accepts (the warn branch) rather than the strict one","Upgrade/refresh any cached release JSON used to build ReleaseAsset"],"exampleFix":"// before\nlet a = ReleaseAsset { name: name.into(), tag: tag.clone(), digest: None };\ndownload(&a).await?;\n// after\nlet a = fetch_asset_from_api(\"owner/repo\", Some(\"v1.2.0\"), name).await?; // digest populated\ndownload(&a).await?;","handlingStrategy":"validation","validationCode":"if asset.digest.is_none() {\n    return Err(format!(\"asset {} sem digest; busque os metadados novamente da API do GitHub\", asset.name));\n}\nlet bytes = github::download(&asset).await?;","typeGuard":"fn has_digest(a: &ReleaseAsset) -> bool {\n    a.digest.is_some()\n}","tryCatchPattern":null,"preventionTips":["Always build ReleaseAsset from fresh API responses, never hand-assemble","Re-fetch release metadata if it came from a cache older than the digest field's introduction","Treat missing digest as a signal to refetch rather than to disable verification"],"tags":["github","integrity","checksum"],"backgroundTag":"checksum-mismatch","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}