{"record":{"id":"4afaf0aef02885f0","repo":"iOfficeAI/AionUi","slug":"update-errors-cdnmanifestinvalid","errorCode":"update.errors.cdnManifestInvalid","errorMessage":"update.errors.cdnManifestInvalid","messagePattern":"update\\.errors\\.cdnManifestInvalid","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/desktop/src/process/bridge/updateBridge.ts","lineNumber":374,"sourceCode":" * single source of truth for \"is there an update\".\n */\nconst fetchCdnManifest = async (): Promise<CdnLatestManifest> => {\n  const url = `${CDN_BASE_URL}/${resolveCdnChannelFile()}`;\n  const controller = new AbortController();\n  const timeoutId = setTimeout(() => controller.abort(), CDN_MANIFEST_TIMEOUT_MS);\n\n  log.info('[manual-update] Checking CDN manifest:', url);\n  try {\n    const res = await fetch(url, {\n      headers: { 'User-Agent': DEFAULT_USER_AGENT },\n      signal: controller.signal,\n    });\n    if (!res.ok) {\n      throw new Error((await getI18n()).t('update.errors.cdnManifestFailed', { status: res.status }));\n    }\n    const manifest = parseCdnManifest(await res.text());\n    if (!manifest) {\n      throw new Error((await getI18n()).t('update.errors.cdnManifestInvalid'));\n    }\n    log.info('[manual-update] CDN manifest resolved:', {\n      url,\n      version: manifest.version,\n      files: manifest.files.length,\n    });\n    return manifest;\n  } catch (err: unknown) {\n    if (err instanceof Error && err.name === 'AbortError') {\n      throw new Error((await getI18n()).t('update.errors.cdnManifestTimeout'), { cause: err });\n    }\n    throw err;\n  } finally {\n    clearTimeout(timeoutId);\n  }\n};\n\ntype ReleaseNotesEnrichment = { body?: string; htmlUrl?: string; name?: string; publishedAt?: string };","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/iOfficeAI/AionUi/blob/711aa0550ee183ea495dc33e2c05c7943b70a60a/packages/desktop/src/process/bridge/updateBridge.ts#L356-L392","documentation":"Thrown when the CDN update manifest was fetched successfully (HTTP 200) but failed to parse via parseCdnManifest. This means the response body is not valid manifest JSON or is missing required fields (version, files). It indicates the CDN is serving an unexpected/corrupted payload.","triggerScenarios":"Calling the update manifest flow where the CDN URL returns 200 with HTML (error page), a JSON object missing `version` or `files`, or malformed JSON that parseCdnManifest rejects.","commonSituations":"CDN misconfiguration serving an index.html fallback, a partially uploaded/truncated manifest file, wrong CDN base URL pointing at a directory listing, or a manifest schema change after an update.","solutions":["Open the resolved CDN manifest URL in a browser/curl and verify it returns valid JSON with `version` and `files`","Check the CDN deployment pipeline for truncated or failed uploads of the manifest","Verify the manifest URL construction in updateBridge.ts (correct base URL, channel and platform path)","Make parseCdnManifest failures log the raw body excerpt so future diagnosis is easier"],"exampleFix":"// before\nconst manifest = parseCdnManifest(await res.text());\n\n// after (diagnose what the CDN actually returned)\nconst text = await res.text();\nconst manifest = parseCdnManifest(text);\nif (!manifest) {\n  log.error('[manual-update] CDN manifest unparsable, first 200 chars:', text.slice(0, 200));\n  throw new Error((await getI18n()).t('update.errors.cdnManifestInvalid'));\n}","handlingStrategy":"validation","validationCode":"const res = await fetch(url);\nconst text = await res.text();\ntry {\n  const parsed = JSON.parse(text);\n  if (typeof parsed.version !== 'string' || !Array.isArray(parsed.files)) {\n    // reject before calling parseCdnManifest-dependent flows\n  }\n} catch { /* not JSON */ }","typeGuard":"type CdnManifest = { version: string; files: Array<{ path: string }> };\nfunction isCdnManifest(v: unknown): v is CdnManifest {\n  if (typeof v !== 'object' || v === null) return false;\n  const o = v as Record<string, unknown>;\n  return typeof o.version === 'string' && Array.isArray(o.files);\n}","tryCatchPattern":"catch (err) { log.error('[update] manifest invalid', { url, bodySnippet }); throw err; }","preventionTips":["Contract-test the manifest schema on every CDN deploy","Log unparsable manifest bodies (truncated) for fast diagnosis","Fail CDN deploys that would publish a manifest without version/files"],"tags":["update","cdn","json-parse","manifest"],"backgroundTag":"invalid-manifest-json","analyzedSha":"711aa0550ee183ea495dc33e2c05c7943b70a60a","analyzedAt":"2026-08-28T07:56:06.558Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}