{"record":{"id":"30eaa4de87197727","repo":"actualbudget/actual","slug":"encrypt-failure-30eaa4","errorCode":"encrypt-failure","errorMessage":"encrypt-failure","messagePattern":"encrypt-failure","errorType":"error_code","errorClass":"FileUploadError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/cloud-storage.ts","lineNumber":320,"sourceCode":"    id,\n    groupId,\n    budgetName,\n    cloudFileId: originalCloudFileId,\n    encryptKeyId,\n  } = prefs.getPrefs();\n  let cloudFileId = originalCloudFileId;\n  let uploadContent = zipContent;\n  let uploadMeta = null;\n\n  // The upload process encrypts with the key tagged in the prefs for\n  // the file. It will upload the file and the server is responsible\n  // for checking that the key is up-to-date and rejecting it if not\n  if (encryptKeyId) {\n    let encrypted;\n    try {\n      encrypted = await encryption.encrypt(zipContent, encryptKeyId);\n    } catch (e) {\n      throw FileUploadError('encrypt-failure', {\n        isMissingKey: e.message === 'missing-key',\n      });\n    }\n    uploadContent = encrypted.value;\n    uploadMeta = encrypted.meta;\n  }\n\n  if (!cloudFileId) {\n    cloudFileId = uuidv4();\n  }\n\n  let res;\n  try {\n    res = await fetchJSON(getServer().SYNC_SERVER + '/upload-user-file', {\n      method: 'POST',\n      headers: {\n        'Content-Length': String(uploadContent.length),\n        'Content-Type': 'application/encrypted-file',","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/cloud-storage.ts#L302-L338","documentation":"FileUploadError('encrypt-failure') is thrown by upload() when end-to-end encryption of the budget archive fails. The file has an encryptKeyId in prefs, and encryption.encrypt(zipContent, encryptKeyId) raised; the error carries isMissingKey: true when the cause was 'missing-key' (the encryption key is not loaded locally).","triggerScenarios":"uploading a budget that has end-to-end encryption enabled while the matching key is not loaded (missing-key), or the key/password is wrong or the crypto operation otherwise throws.","commonSituations":"restoring prefs/cloudFileId on a new device without re-entering the encryption key, changed encryption password on another device, corrupted key in the key store, or server URL switched so the stored keyId no longer matches any loaded key.","solutions":["If isMissingKey is true, load the encryption key (Settings → encryption key, or api.loadKey) before retrying","Verify the key id in metadata.json matches a key you possess; re-enter the original password","If the key is truly lost, disable/recreate encryption: download without key is impossible, so restore from local backup and set a new key, then re-upload","Check that cloudFileId/encryptKeyId prefs are consistent; clear stale prefs and re-link the cloud file"],"exampleFix":"// before\nawait uploadBudget();\n// after\ntry {\n  await uploadBudget();\n} catch (e) {\n  if (e.reason === 'encrypt-failure' && e.isMissingKey) {\n    await loadKey(password);\n    await uploadBudget();\n  } else { throw e; }\n}","handlingStrategy":"try-catch","validationCode":"async function ensureKeyLoaded(encryptKeyId) {\n  if (encryptKeyId && !encryption.hasKey(encryptKeyId)) {\n    throw new Error(`encryption key ${encryptKeyId} not loaded; call loadKey(password) first`);\n  }\n}","typeGuard":"function canEncrypt(prefs, hasKeyFn = id => id != null) {\n  return !prefs?.encryptKeyId || hasKeyFn(prefs.encryptKeyId);\n}","tryCatchPattern":"try {\n  await uploadBudget();\n} catch (e) {\n  if (e instanceof FileUploadError && e.reason === 'encrypt-failure') {\n    if (e.isMissingKey) { await loadKey(keyPassword); return uploadBudget(); }\n    throw new Error('encryption failed: check key/password', { cause: e });\n  }\n  throw e;\n}","preventionTips":["Load the encryption key on every new device before syncing","When changing the password, update it on all clients first","Record the key password securely — lost keys make cloud files undecryptable","Check metadata.json encryptKeyId against loaded keys before uploading"],"tags":["encryption","missing-key","cloud-sync","security"],"backgroundTag":"missing-encryption-key","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}