{"record":{"id":"ad81ececdbedb174","repo":"actualbudget/actual","slug":"failed-to-get-server-config","errorCode":null,"errorMessage":"Failed to get server config.","messagePattern":"Failed to get server config\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/accounts/app.ts","lineNumber":735,"sourceCode":"\nasync function setSecret({\n  name,\n  value,\n  fileId = null,\n}: {\n  name: string;\n  value: string | null;\n  fileId?: string | null;\n}) {\n  const userToken = await asyncStorage.getItem('user-token');\n\n  if (!userToken) {\n    return { error: 'unauthorized' };\n  }\n\n  const serverConfig = getServer();\n  if (!serverConfig) {\n    throw new Error('Failed to get server config.');\n  }\n\n  const headers = {\n    'X-ACTUAL-TOKEN': userToken,\n    ...(fileId ? { 'X-Actual-File-Id': fileId } : {}),\n  };\n\n  try {\n    if (value === null) {\n      return await del(\n        serverConfig.BASE_SERVER + '/secret/' + name,\n        {},\n        headers,\n      );\n    }\n\n    return await post(\n      serverConfig.BASE_SERVER + '/secret',","sourceCodeStart":717,"sourceCodeEnd":753,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/accounts/app.ts#L717-L753","documentation":"setSecret stores a named secret (e.g. bank-sync credentials) on the sync server. It reads the global server config via getServer(); when the app is running without a configured server URL, getServer() returns null and this error is thrown because there is no server to store the secret on.","triggerScenarios":"Calling the 'secret-set' handler (setSecret) while the client is in local-only mode ('Don't use a server') or before setServer(url) has been called, so the module-level config in server-config.ts is still null.","commonSituations":"Using a local-only budget but attempting to configure bank sync (GoCardless/SimpleFin/Pluggy.ai/Akahu) which requires a server; forgetting to run actual.init with serverUrl in the API package; the server URL was reset via setServer(null); standalone/desktop app started without a server URL.","solutions":["Configure a sync server URL: set it in the app's advanced settings or call actual.init({ serverURL: 'https://your-server' }) in the API.","If self-hosting, start the sync server and point the client at it before using secrets/bank sync.","Check that nothing called setServer(null) or reset the server URL after initialization.","If you intentionally run local-only, avoid secret-set/bank-sync APIs — they require a server."],"exampleFix":"// before\nawait actual.init({ dataDir: './data' });\nawait actual.q('messages').insert(...); // then later setSecret fails\n// after\nawait actual.init({ dataDir: './data', serverURL: 'https://actual.example.com' });\nawait actual.login('user@example.com', 'password');\n// now setSecret works","handlingStrategy":"validation","validationCode":"// read the same config the app uses\nimport { getServer } from './server-config';\nif (!getServer()) {\n  throw new Error('No sync server configured; set a server URL before storing secrets.');\n}","typeGuard":"function hasServerConfig(c: ReturnType<typeof getServer>): c is NonNullable<ReturnType<typeof getServer>> {\n  return c != null && typeof c.BASE_SERVER === 'string';\n}","tryCatchPattern":"try {\n  await send('secret-set', { name, value });\n} catch (e) {\n  if (e.message === 'Failed to get server config.') {\n    // local-only mode: surface a 'server required' state instead of crashing\n    reportServerRequired(name);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Always pass serverURL to actual.init when using server-backed features.","Check server connection status in the UI before exposing secret/bank-sync settings.","Treat local-only mode as a first-class state: hide or disable server-only handlers.","Log the configured server URL at startup to catch empty-config issues early."],"tags":["configuration","sync-server","local-only"],"backgroundTag":"server-not-configured","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}