iOfficeAI/AionUi · error · Error
update.errors.githubApiNotArray
update.errors.githubApiNotArray
Error message
update.errors.githubApiNotArray
What it means
Thrown by fetchGitHubReleases when the GitHub Releases API responds 200 OK but the JSON body is not an array. The GitHub releases endpoint is expected to return a JSON array of release objects; any other top-level shape (usually an error object) fails this type check after res.json().
Source
Thrown at packages/desktop/src/process/bridge/updateBridge.ts:337
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const res = await fetch(url, {
headers: {
Accept: 'application/vnd.github+json',
'User-Agent': DEFAULT_USER_AGENT,
},
signal: controller.signal,
});
if (!res.ok) {
throw new Error((await getI18n()).t('update.errors.githubApiFailed', { status: res.status }));
}
const json = (await res.json()) as unknown;
if (!Array.isArray(json)) {
throw new Error((await getI18n()).t('update.errors.githubApiNotArray'));
}
return json as GitHubReleaseApi[];
} catch (err: unknown) {
if (err instanceof Error && err.name === 'AbortError') {
throw new Error((await getI18n()).t('update.errors.githubApiTimeout'), { cause: err });
}
throw err;
} finally {
clearTimeout(timeoutId);
}
};
const CDN_MANIFEST_TIMEOUT_MS = 15000;
const GITHUB_NOTES_TIMEOUT_MS = 10000;
/**
* Fetch and parse the authoritative CDN channel manifest for the current
* platform/arch. Any failure here fails the manual check — the CDN is theView on GitHub (pinned to 711aa0550e)
Solutions
- Log/inspect the actual response body to see what came back instead of an array
- If a proxy is rewriting responses, bypass it or allowlist api.github.com
- Retry — transient proxy interference often clears
- If the endpoint shape changed upstream, update fetchGitHubReleases parsing to match the documented API
Example fix
// before
const json = (await res.json()) as unknown;
if (!Array.isArray(json)) throw ...;
// after
const json = (await res.json()) as unknown;
if (!Array.isArray(json)) {
log.warn('[update] unexpected GitHub API body:', JSON.stringify(json).slice(0, 200));
throw ...;
} Defensive patterns
Strategy: type-guard
Type guard
const isReleaseArray = (v: unknown): v is GitHubReleaseApi[] => Array.isArray(v) && v.every((r) => typeof r === 'object' && r !== null && 'tag_name' in r);
Try / catch
try {
const releases = await fetchGitHubReleases(repo);
} catch (err) {
if (err instanceof Error && err.message.includes('githubApiNotArray')) {
// likely proxy interference; log body context and fall back to CDN manifest
} else throw err;
} Prevention
- Log the raw body when shape validation fails
- Bypass corporate proxies for api.github.com or pin them via allowlist
- Pin tests to GitHub's documented schema to catch API drift
When it happens
Trigger: A 200 response whose body is an object — commonly a proxy/interceptor returning {error: ...}, a captive portal returning an HTML/JSON hybrid with 200, or a GitHub API change in response shape. Also possible if the URL was rewritten (e.g. enterprise proxy) to a different JSON endpoint.
Common situations: Corporate proxies returning 200 with a JSON error body; DNS hijacking/captive portals; API version drift on GitHub's side; a repo redirect that returns an object message instead of the releases array.
Related errors
- update.errors.githubApiFailed
- update.errors.githubApiTimeout
- update.errors.invalidUrl
- update.errors.httpsOnly
- update.errors.hostNotAllowed
AI-assisted analysis of iOfficeAI/AionUi@711aa0550e (2026-08-28).
Data as JSON: /api/errors/49b07064cfe9dfb0.
Report an issue: GitHub.