can1357/oh-my-pi · error · Error
GitHub release ${expectedTag} has no asset list
Error message
GitHub release ${expectedTag} has no asset list What it means
resolveReleaseBinaryAsset validates raw GitHub API release metadata before selecting the update binary. The fetched release object did not include an `assets` array, so no binary could be located. This guards against malformed or unexpected API responses (the code treats release metadata as untrusted `unknown`).
Source
Thrown at packages/coding-agent/src/cli/update-cli.ts:209
release: unknown,
expectedTag: string,
binaryName: string,
options: { allowPrerelease?: boolean } = {},
): ReleaseBinaryAsset {
if (!isRecord(release)) {
throw new Error("Invalid GitHub release metadata");
}
if (release.tag_name !== expectedTag) {
throw new Error(`GitHub release tag mismatch: expected ${expectedTag}`);
}
if (release.draft !== false) {
throw new Error(`GitHub release ${expectedTag} is a draft, not a published release`);
}
if (release.prerelease !== false && !options.allowPrerelease) {
throw new Error(`GitHub release ${expectedTag} is a prerelease; only canary updates install prerelease assets`);
}
if (!Array.isArray(release.assets)) {
throw new Error(`GitHub release ${expectedTag} has no asset list`);
}
const matches = release.assets.filter(asset => isRecord(asset) && asset.name === binaryName);
if (matches.length !== 1) {
throw new Error(`GitHub release ${expectedTag} has ${matches.length} assets named ${binaryName}`);
}
const asset = matches[0];
if (!isRecord(asset) || asset.state !== "uploaded") {
throw new Error(`GitHub release asset ${binaryName} is not fully uploaded`);
}
if (typeof asset.size !== "number" || !Number.isSafeInteger(asset.size) || asset.size <= 0) {
throw new Error(`GitHub release asset ${binaryName} has an invalid size`);
}
if (typeof asset.digest !== "string") {
throw new Error(`GitHub release asset ${binaryName} has no digest`);
}
const digest = /^sha256:([0-9a-f]{64})$/i.exec(asset.digest)?.[1];View on GitHub (pinned to 9690622007)
Solutions
- Re-run the update to refetch release metadata; transient truncation is rare but possible.
- Verify the GitHub API response manually: curl -H 'X-GitHub-Api-Version: 2022-11-28' https://api.github.com/repos/<owner>/<repo>/releases/tags/<tag> and confirm an assets array exists.
- Check whether a proxy/interceptor or custom fetchImpl is altering the response.
- If GitHub changed its API shape, update the validation in packages/coding-agent/src/cli/update-cli.ts.
Example fix
// before (test stub missing assets)
fetchImpl = async () => new Response(JSON.stringify({ tag_name: "v1.2.3", draft: false, prerelease: false }), { headers: { "content-type": "application/json" } });
// after
fetchImpl = async () => new Response(JSON.stringify({ tag_name: "v1.2.3", draft: false, prerelease: false, assets: [{ name: binaryName, state: "uploaded", size: 1024, digest: "sha256:" + "a".repeat(64), browser_download_url: expectedUrl }] }), { headers: { "content-type": "application/json" } }); Defensive patterns
Strategy: type-guard
Validate before calling
const releaseRes = await fetch(`https://api.github.com/repos/${REPO}/releases/tags/${tag}`, { headers: { "X-GitHub-Api-Version": "2022-11-28" } });
const release = await releaseRes.json();
if (!releaseRes.ok || !Array.isArray(release?.assets)) throw new Error("Release metadata malformed; aborting update"); Type guard
function hasAssets(release: unknown): release is { assets: Record<string, unknown>[] } {
return typeof release === "object" && release !== null && Array.isArray((release as { assets?: unknown }).assets);
} Try / catch
try {
await update();
} catch (err) {
if (err instanceof Error && err.message.includes("has no asset list")) {
console.error("GitHub returned malformed release metadata; retry or check api.github.com directly.");
} else throw err;
} Prevention
- Always pass the X-GitHub-Api-Version header when calling the GitHub API to pin the response schema.
- Validate parsed JSON shape before consuming it; treat API responses as untrusted.
- In tests, build release stubs with a full assets array.
When it happens
Trigger: getReleaseBinaryAsset calls GET /repos/<REPO>/releases/tags/<tag> and passes the parsed JSON to resolveReleaseBinaryAsset; the error throws when `release.assets` is not an array (missing, null, or a non-array shape) while tag/draft/prerelease checks already passed.
Common situations: A GitHub API schema change or API-version drift (X-GitHub-Api-Version), a mocked/stubbed fetch in tests returning a partial release object, a corporate proxy or mirror rewriting/stripping the response body, or a hand-built release object missing assets.
Related errors
- Timed out fetching GitHub release metadata after 30s
- GitHub API rate limit exceeded while fetching release metada
- GitHub release ${expectedTag} has ${matches.length} assets n
- GitHub release asset ${binaryName} is not fully uploaded
- GitHub release asset ${binaryName} has an invalid size
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2ef3cb27c7456c21.
Report an issue: GitHub.