{"record":{"id":"50cdaba4233e5c9c","repo":"actualbudget/actual","slug":"load-key-error","errorCode":"load-key-error","errorMessage":"load-key-error","messagePattern":"load-key-error","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/main.ts","lineNumber":208,"sourceCode":"  await sqlite.init();\n  asyncStorage.init();\n  await fs.init();\n  await setupDocumentsDir();\n\n  const keysStr = await asyncStorage.getItem('encrypt-keys');\n  if (keysStr) {\n    try {\n      const keys = JSON.parse(keysStr);\n\n      // Load all the keys\n      await Promise.all(\n        Object.keys(keys).map(fileId => {\n          return encryption.loadKey(keys[fileId]);\n        }),\n      );\n    } catch (e) {\n      logger.log('Error loading key', e);\n      throw new Error('load-key-error');\n    }\n  }\n\n  const url = await asyncStorage.getItem('server-url');\n\n  if (!url) {\n    await asyncStorage.removeItem('user-token');\n  }\n  setServer(url);\n\n  connection.init(socketName, app.handlers);\n\n  // Allow running DB queries locally\n  global.$query = aqlQuery;\n  global.$q = q;\n\n  if (isDev) {\n    global.$send = (name, args) => runHandler(app.handlers[name], args);","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/main.ts#L190-L226","documentation":"During initApp, Actual loads per-file encryption keys from the sync server via encryption.loadKey. If any key fails to load or decrypt (wrong password, corrupt key blob, key missing), the error is logged and this coded error is thrown so the client can map it to a user-facing message.","triggerScenarios":"Opening a budget whose encryption key file on the server cannot be decrypted — e.g. the user changed or forgot the budget's end-to-end encryption password, or the key record is missing/corrupt in the server's key store.","commonSituations":"Changing the encryption password in one client while another client still caches the old one; restoring a server from backup without the key files; server data dir partially deleted; decrypting with a different password than the one used to encrypt.","solutions":["Enter the correct encryption password for the budget when prompted (the key was encrypted with the original password).","If the password was changed, re-set it from the client that can still open the budget so the server key matches.","Check the server logs for the logged 'Error loading key' detail to distinguish corrupt key data from a wrong password.","If the key is unrecoverable, restore the affected files/key store from a server backup, or reset end-to-end encryption (accepting that old encrypted data may be lost)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before opening a budget, confirm the server is reachable and key exists\nconst res = await fetch(`${serverUrl}/status`);\nif (!res.ok) throw new Error('sync server unreachable');","typeGuard":null,"tryCatchPattern":"try {\n  await initApp();\n} catch (e) {\n  if (e.message === 'load-key-error') {\n    // prompt user for the budget's encryption password or check server key files\n  } else throw e;\n}","preventionTips":["Never change a budget's encryption password from multiple clients independently.","Back up the server's user-files/key store together with budget data.","Keep all clients on the same Actual version when using end-to-end encryption.","Check server logs ('Error loading key') at the first sign of decryption failures."],"tags":["encryption","sync-server","keys"],"backgroundTag":"encryption-key-load-failed","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}