{"record":{"id":"55b27ae84d8f2136","repo":"can1357/oh-my-pi","slug":"github-release-asset-binaryname-has-no-digest","errorCode":null,"errorMessage":"GitHub release asset ${binaryName} has no digest","messagePattern":"GitHub release asset (.+?) has no digest","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/cli/update-cli.ts","lineNumber":225,"sourceCode":"\t}\n\tif (!Array.isArray(release.assets)) {\n\t\tthrow new Error(`GitHub release ${expectedTag} has no asset list`);\n\t}\n\n\tconst matches = release.assets.filter(asset => isRecord(asset) && asset.name === binaryName);\n\tif (matches.length !== 1) {\n\t\tthrow new Error(`GitHub release ${expectedTag} has ${matches.length} assets named ${binaryName}`);\n\t}\n\n\tconst asset = matches[0];\n\tif (!isRecord(asset) || asset.state !== \"uploaded\") {\n\t\tthrow new Error(`GitHub release asset ${binaryName} is not fully uploaded`);\n\t}\n\tif (typeof asset.size !== \"number\" || !Number.isSafeInteger(asset.size) || asset.size <= 0) {\n\t\tthrow new Error(`GitHub release asset ${binaryName} has an invalid size`);\n\t}\n\tif (typeof asset.digest !== \"string\") {\n\t\tthrow new Error(`GitHub release asset ${binaryName} has no digest`);\n\t}\n\tconst digest = /^sha256:([0-9a-f]{64})$/i.exec(asset.digest)?.[1];\n\tif (!digest) {\n\t\tthrow new Error(`GitHub release asset ${binaryName} has an unsupported digest`);\n\t}\n\n\tconst expectedUrl = `https://github.com/${REPO}/releases/download/${expectedTag}/${binaryName}`;\n\tif (asset.browser_download_url !== expectedUrl) {\n\t\tthrow new Error(`GitHub release asset ${binaryName} has an unexpected download URL`);\n\t}\n\n\treturn {\n\t\turl: expectedUrl,\n\t\tsize: asset.size,\n\t\tdigest: `sha256:${digest.toLowerCase()}`,\n\t};\n}\n","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/cli/update-cli.ts#L207-L243","documentation":"The updater verifies downloads against the asset's sha256 digest, which GitHub exposes on the asset record as a `digest` string. If the field is absent, integrity verification is impossible, so the update is refused rather than installing an unverified binary.","triggerScenarios":"resolveReleaseBinaryAsset throws when the matched asset has `typeof asset.digest !== \"string\"` — the digest field is missing, null, or a non-string value.","commonSituations":"Releases created before GitHub started populating asset digests, custom upload tooling or API-created assets without digests, proxies stripping fields, or older pinned API versions that predate the digest field.","solutions":["Confirm you are sending the supported API version header (X-GitHub-Api-Version: 2022-11-28) and refetch.","Check the asset in the GitHub API response — if digest is absent, re-upload the asset so GitHub computes a digest.","Retry later: digest population can briefly lag upload on very fresh releases.","Avoid proxies/interceptors that rewrite the response body."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const asset = release.assets?.find(a => a?.name === binaryName);\nif (typeof asset?.digest !== \"string\") {\n  console.warn(\"Asset has no digest; refusing unverified download.\");\n  process.exit(1);\n}","typeGuard":"function hasDigest(asset: unknown): asset is { digest: string } {\n  return typeof (asset as { digest?: unknown })?.digest === \"string\";\n}","tryCatchPattern":"try {\n  await update();\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"has no digest\")) {\n    console.error(\"GitHub did not provide a checksum for this asset; update refused for safety. Re-upload the asset or retry later.\");\n  } else throw err;\n}","preventionTips":["Only download binaries whose published checksum you can verify.","Re-upload stale assets created before GitHub populated digests.","Send the 2022-11-28 API-version header so digest fields are present."],"tags":["github-api","checksum","update","integrity"],"backgroundTag":"missing-checksum","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}