{"record":{"id":"b9b8cc283a87497d","repo":"can1357/oh-my-pi","slug":"github-release-asset-binaryname-has-an-unexpect","errorCode":null,"errorMessage":"GitHub release asset ${binaryName} has an unexpected download URL","messagePattern":"GitHub release asset (.+?) has an unexpected download URL","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/cli/update-cli.ts","lineNumber":234,"sourceCode":"\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\nasync function getReleaseBinaryAsset(\n\texpectedVersion: string,\n\tbinaryName: string,\n\tfetchImpl: Fetch = fetch,\n\tgithubToken: string | undefined = $env.GITHUB_TOKEN || $env.GH_TOKEN,\n\tallowPrerelease = false,\n): Promise<ReleaseBinaryAsset> {\n\tconst tag = `v${expectedVersion}`;\n\tconst headers: Record<string, string> = {","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/cli/update-cli.ts#L216-L252","documentation":"The updater computes the canonical download URL (`https://github.com/<REPO>/releases/download/<tag>/<binaryName>`) and requires the asset's `browser_download_url` to match exactly. A mismatch means the asset would be served from somewhere other than the expected path — possibly a redirect, mirror, or spoofed entry — so the update aborts as a supply-chain safeguard.","triggerScenarios":"resolveReleaseBinaryAsset throws when `asset.browser_download_url !== expectedUrl` for the uniquely matched asset — the URL points to a different repo, tag, host, or filename.","commonSituations":"A repository fork or rename making GitHub return adjusted URLs, a proxy/enterprise mirror rewriting URLs, test stubs with wrong URLs, or assets uploaded to a differently-named release while the tag check passed.","solutions":["Compare the asset's browser_download_url in the API response against the expected https://github.com/<owner>/<repo>/releases/download/<tag>/<name> pattern.","Remove or bypass any proxy/mirror rewriting download URLs and retry.","If the repo was transferred/renamed, update REPO in update-cli.ts or follow redirects properly.","Fix test stubs to build the URL with the same REPO/tag/binaryName the resolver expects."],"exampleFix":"// before (stub URL for wrong repo)\nbrowser_download_url: \"https://github.com/fork/omp/releases/download/v1.2.3/omp-linux-x64\"\n// after\nbrowser_download_url: `https://github.com/${REPO}/releases/download/v1.2.3/omp-linux-x64`","handlingStrategy":"validation","validationCode":"const expectedUrl = `https://github.com/${REPO}/releases/download/${tag}/${binaryName}`;\nconst asset = release.assets?.find(a => a?.name === binaryName);\nif (asset?.browser_download_url !== expectedUrl) {\n  throw new Error(`Download URL mismatch: expected ${expectedUrl}, got ${asset?.browser_download_url}`);\n}","typeGuard":"function hasExpectedUrl(asset: unknown, expectedUrl: string): boolean {\n  return (asset as { browser_download_url?: unknown })?.browser_download_url === expectedUrl;\n}","tryCatchPattern":"try {\n  await update();\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"unexpected download URL\")) {\n    console.error(\"Asset download URL does not match the canonical release path; possible mirror/rewrite. Bypass proxies and retry.\");\n  } else throw err;\n}","preventionTips":["Pin updates to canonical github.com download URLs; treat rewrites as hostile.","Disable response-rewriting proxies for api.github.com and github.com downloads.","Update REPO constants after repo transfers/renames."],"tags":["github-api","supply-chain","update","url-validation"],"backgroundTag":"unexpected-download-url","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}