{"record":{"id":"965ff1ec8c825f37","repo":"actualbudget/actual","slug":"getdownloaderror-result-error","errorCode":null,"errorMessage":"getDownloadError(result.error)","messagePattern":"getDownloadError\\(result\\.error\\)","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/api.ts","lineNumber":258,"sourceCode":"    const result = await handlers['sync-budget']();\n    if (result.error) {\n      throw withErrorCode(\n        new Error(\n          getSyncError(result.error.reason, localBudget.id, result.error.meta),\n        ),\n        result.error.reason,\n      );\n    }\n    return;\n  }\n\n  // Download the remote file (no need to perform a sync as the file will already be up-to-date)\n  const result = await handlers['download-budget']({\n    cloudFileId: remoteBudget.fileId,\n  });\n  if (result.error) {\n    logger.log('Full error details', result.error);\n    throw withErrorCode(\n      new Error(getDownloadError(result.error)),\n      result.error.reason,\n    );\n  }\n  await handlers['load-budget']({ id: result.id });\n};\n\nhandlers['api/get-budgets'] = async function () {\n  const budgets = await handlers['get-budgets']();\n  const files = (await handlers['get-remote-files']()) || [];\n  return [\n    ...budgets.map(file => budgetModel.toExternal(file)),\n    ...files.map(file => remoteFileModel.toExternal(file)).filter(file => file),\n  ];\n};\n\nhandlers['api/sync'] = async function () {\n  const { id } = prefs.getPrefs();","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/api.ts#L240-L276","documentation":"Thrown by api/download-budget in packages/loot-core/src/server/api.ts when handlers['download-budget'] fails to fetch and import the remote file. getDownloadError() (packages/loot-core/src/shared/errors.ts:76) maps reasons — network/download-failure, invalid zip/meta, zip-too-large, decrypt-failure, out-of-sync-migrations, clock-drift — to actionable messages, and the reason becomes the error code. Full details are logged via logger before throwing.","triggerScenarios":"downloadBudget(syncId) reaching the actual cloud download step and failing: server unreachable mid-transfer, corrupted/partial zip stored server-side, archive exceeding size limits, wrong password for E2E-encrypted file, or clock drift detected during transfer.","commonSituations":"Self-hosted server data directory with truncated .zip files; budget exceeding max file size; unstable network in CI pipelines; encryption password changed between key-test and download; container clock skew.","solutions":["Check the logged 'Full error details' and the error.code for the precise reason","For network/download-failure, verify server health and retry","For decrypt-failure, supply the correct password to downloadBudget","For zip-too-large/invalid-zip, inspect the server's stored file and restore a clean copy from a working client"],"exampleFix":"// before\nawait api.downloadBudget(syncId);\n// after\ntry {\n  await api.downloadBudget(syncId, { password });\n} catch (e) {\n  if (['network', 'download-failure'].includes(e.code)) await retry(() => api.downloadBudget(syncId, { password }), 3);\n  else throw e;\n}","handlingStrategy":"retry","validationCode":"const res = await fetch(`${serverUrl}/health`);\nif (!res.ok) throw new Error('Server unhealthy before download');","typeGuard":"function isDownloadReason(r: string): boolean {\n  return ['network','download-failure','not-zip-file','invalid-zip-file','invalid-meta-file','zip-too-large','decrypt-failure','out-of-sync-migrations','clock-drift'].includes(r);\n}","tryCatchPattern":"try {\n  await api.downloadBudget(syncId, { password });\n} catch (e) {\n  if (['network', 'download-failure'].includes(e.code)) {\n    await backoffRetry(() => api.downloadBudget(syncId, { password }), 3);\n  } else if (e.code === 'decrypt-failure') {\n    throw new Error('Bad password: ' + getDownloadError(e));\n  } else throw e;\n}","preventionTips":["Log and inspect error.code — getDownloadError maps each reason to a specific fix","Retry network/download-failure with exponential backoff","Monitor server disk integrity; truncated zips cause invalid-zip-file errors","Keep clocks synced and app updated to avoid clock-drift and migration failures"],"tags":["download","sync-server","zip"],"backgroundTag":"budget-download-failed","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}