{"record":{"id":"61267ae16a48d420","repo":"iOfficeAI/AionUi","slug":"update-errors-downloadfailed","errorCode":"update.errors.downloadFailed","errorMessage":"update.errors.downloadFailed","messagePattern":"update\\.errors\\.downloadFailed","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/desktop/src/process/bridge/updateBridge.ts","lineNumber":523,"sourceCode":"      downloadId,\n      status,\n      receivedBytes,\n      totalBytes,\n      percent,\n      bytesPerSecond,\n    });\n  };\n\n  emitThrottled('starting');\n\n  log.info('[update-download] Downloading from URL:', url);\n\n  let stream: fs.WriteStream | null = null;\n  try {\n    const res = await fetchWithAllowlistedRedirects(url, abortController.signal);\n\n    if (!res.ok) {\n      throw new Error((await getI18n()).t('update.errors.downloadFailed', { status: res.status }));\n    }\n\n    const contentLengthHeader = res.headers.get('content-length');\n    if (contentLengthHeader) {\n      const parsed = parseInt(contentLengthHeader, 10);\n      if (Number.isFinite(parsed) && parsed > 0) {\n        totalBytes = parsed;\n      }\n    }\n\n    if (!res.body) {\n      throw new Error((await getI18n()).t('update.errors.downloadNoBody'));\n    }\n\n    stream = fs.createWriteStream(file_path);\n    const reader = res.body.getReader();\n\n    let doneReading = false;","sourceCodeStart":505,"sourceCodeEnd":541,"githubUrl":"https://github.com/iOfficeAI/AionUi/blob/711aa0550ee183ea495dc33e2c05c7943b70a60a/packages/desktop/src/process/bridge/updateBridge.ts#L505-L541","documentation":"Thrown when downloading an update artifact fails at the HTTP level: the response status is not ok (non-2xx). The status code is interpolated into the i18n message.","triggerScenarios":"fetchWithAllowlistedRedirects returns 403/404/5xx for the artifact URL — e.g. the file was removed from the CDN, the URL expired (signed link), or the server errored.","commonSituations":"Artifact deleted/rotated on the CDN while the manifest still references it, expired pre-signed URLs, region-blocked downloads, or CDN misconfiguration. 404 after a new release rollout is typical.","solutions":["Retry the download — transient CDN 5xx or artifact propagation delay is common right after a release","Verify the artifact URL from the manifest opens directly (curl -I) and check the status code","Re-fetch the manifest to get a fresh URL in case the old one was rotated","If 403, check CDN auth/signature configuration and whether the download host requires headers"],"exampleFix":"// before\nconst res = await fetchWithAllowlistedRedirects(url, abortController.signal);\nif (!res.ok) {\n  throw new Error((await getI18n()).t('update.errors.downloadFailed', { status: res.status }));\n}\n\n// after (one retry for transient failures)\nlet res = await fetchWithAllowlistedRedirects(url, abortController.signal);\nif ((res.status === 404 || res.status >= 500) && !retried) {\n  retried = true;\n  await new Promise((r) => setTimeout(r, 2000));\n  res = await fetchWithAllowlistedRedirects(url, abortController.signal);\n}\nif (!res.ok) {\n  throw new Error((await getI18n()).t('update.errors.downloadFailed', { status: res.status }));\n}","handlingStrategy":"retry","validationCode":"const head = await fetch(artifactUrl, { method: 'HEAD' });\nif (!head.ok) { /* don't start the download; surface status */ }","typeGuard":null,"tryCatchPattern":"catch (err) {\n  if (err instanceof Error && err.message.includes('downloadFailed')) {\n    await backoffRetry(); // one retry for 404/5xx after fresh manifest fetch\n  }\n  throw err;\n}","preventionTips":["HEAD-check artifact URLs before starting large downloads","Re-fetch the manifest right before downloading to get fresh URLs","Alert on CDN 4xx rates for artifact paths"],"tags":["update","download","http-status","cdn"],"backgroundTag":"download-http-error","analyzedSha":"711aa0550ee183ea495dc33e2c05c7943b70a60a","analyzedAt":"2026-08-28T07:56:06.558Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}