{"record":{"id":"e2ff69a8a76f5e41","repo":"actualbudget/actual","slug":"document-directory-is-not-set","errorCode":null,"errorMessage":"Document directory is not set","messagePattern":"Document directory is not set","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/loot-core/src/platform/server/fs/shared.ts","lineNumber":9,"sourceCode":"// @ts-strict-ignore\nimport { join } from '#platform/server/fs/path-join';\n\nlet documentDir;\nexport const _setDocumentDir = dir => (documentDir = dir);\n\nexport const getDocumentDir = () => {\n  if (!documentDir) {\n    throw new Error('Document directory is not set');\n  }\n  return documentDir;\n};\n\nexport const getBudgetDir = id => {\n  if (!id) {\n    throw new Error('getDocumentDir: id is falsy: ' + id);\n  }\n\n  // TODO: This should be better\n  //\n  // A cheesy safe guard. The id is generated from the budget name,\n  // so it provides an entry point for the user to accidentally (or\n  // intentionally) access other parts of the system. Always\n  // restrict it to only access files within the budget directory by\n  // never allowing slashes.\n  if (id.match(/[^A-Za-z0-9\\-_]/)) {\n    throw new Error(","sourceCodeStart":1,"sourceCodeEnd":27,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/platform/server/fs/shared.ts#L1-L27","documentation":"Actual's server filesystem layer stores the user's chosen document (budget data) directory in a module-level `documentDir` variable, set via `_setDocumentDir`. `getDocumentDir` throws this error when any code tries to resolve a file path before that directory has been configured. It signals a lifecycle bug: filesystem APIs were used before app initialization set the root directory.","triggerScenarios":"Calling any fs/path helper that resolves through getDocumentDir (e.g. getBudgetDir) before `_setDocumentDir(dir)` has been invoked during app/budget initialization; resetting the module state in tests and forgetting to re-set the dir; importing server fs code into a context that never runs the init sequence.","commonSituations":"Running loot-core server code in a script or test harness that skipped the normal bootstrap; calling budget-loading APIs early in Electron/renderer startup; a code path that cleared documentDir (e.g. after closing a budget) but still resolves paths.","solutions":["Ensure `_setDocumentDir(dir)` (or the app's init/bootstrap that calls it) runs before any filesystem or budget-path call.","If writing a custom script/test, call `_setDocumentDir` explicitly with a writable temp/app directory at setup.","Check for code that resets or clears documentDir mid-session and re-set it after that point."],"exampleFix":"// before\nimport { getBudgetDir } from './shared';\nconst dir = getBudgetDir(budgetId);\n\n// after\nimport { _setDocumentDir, getBudgetDir } from './shared';\n_setDocumentDir('/home/user/.actual');\nconst dir = getBudgetDir(budgetId);","handlingStrategy":"validation","validationCode":"import { _setDocumentDir } from './shared';\n// at bootstrap, before any fs call:\nif (!process.env.ACTUAL_DOCUMENT_DIR) {\n  throw new Error('Set the document directory before initializing Actual');\n}\n_setDocumentDir(process.env.ACTUAL_DOCUMENT_DIR);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always run the app bootstrap/init sequence before touching budget-path APIs.","In tests, set the document dir in a shared beforeEach/setup fixture.","Never clear documentDir without immediately re-setting it."],"tags":["initialization","filesystem","lifecycle","server"],"backgroundTag":"missing-env-var","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}