actualbudget/actual · error · Error
getDocumentDir: id is falsy: ${id}
Error message
getDocumentDir: id is falsy: ${id} What it means
getBudgetDir resolves the on-disk directory `<documentDir>/<budgetId>` and refuses to run when the budget id is falsy (undefined, null, empty string). This guards against silently writing budget files into the document root itself. Note the message text says 'getDocumentDir' for historical reasons but it comes from getBudgetDir.
Source
Thrown at packages/loot-core/src/platform/server/fs/shared.ts:16
// @ts-strict-ignore
import { join } from '#platform/server/fs/path-join';
let documentDir;
export const _setDocumentDir = dir => (documentDir = dir);
export const getDocumentDir = () => {
if (!documentDir) {
throw new Error('Document directory is not set');
}
return documentDir;
};
export const getBudgetDir = id => {
if (!id) {
throw new Error('getDocumentDir: id is falsy: ' + id);
}
// TODO: This should be better
//
// A cheesy safe guard. The id is generated from the budget name,
// so it provides an entry point for the user to accidentally (or
// intentionally) access other parts of the system. Always
// restrict it to only access files within the budget directory by
// never allowing slashes.
if (id.match(/[^A-Za-z0-9\-_]/)) {
throw new Error(
`Invalid budget id "${id}". Check the id of your budget in the Advanced section of the settings page.`,
);
}
return join(getDocumentDir(), id);
};
View on GitHub (pinned to d4334cb6e6)
Solutions
- Validate the budget id is a non-empty string before calling any API that resolves a budget directory.
- Trace where the id comes from (budget list, localStorage, query param) and fix the source returning an empty value.
- Only call getBudgetDir after a budget has been explicitly loaded/created so a valid id exists.
Example fix
// before
const dir = getBudgetDir(budget?.id);
// after
if (!budget?.id) {
throw new Error('No budget loaded: cannot resolve budget directory');
}
const dir = getBudgetDir(budget.id); Defensive patterns
Strategy: validation
Validate before calling
function assertBudgetId(id) {
if (typeof id !== 'string' || id.length === 0) {
throw new Error(`Budget id must be a non-empty string, got: ${JSON.stringify(id)}`);
}
} Type guard
function isBudgetId(id: unknown): id is string {
return typeof id === 'string' && id.length > 0;
} Try / catch
try {
const dir = getBudgetDir(id);
} catch (e) {
if (String(e.message).includes('id is falsy')) {
// no budget loaded — prompt user to open/create one
} else throw e;
} Prevention
- Only call budget-path APIs while a budget is loaded.
- Guard optional ids (budget?.id) at call sites.
- Load budget ids from a single source of truth and validate on read.
When it happens
Trigger: Calling getBudgetDir(undefined/null/'') — typically when a budget id failed to load, a caller passed an unset variable, or a remote-id lookup returned nothing before the directory is computed.
Common situations: A budget list returned no id for the requested budget; migration/import code computing a directory before the id is assigned; tests constructing budgets without ids; a stale UI passing a cleared id after budget close.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Document directory is not set
- Invalid budget id "${id}". Check the id of your budget in th
- error
- Invalid --name: must be a non-empty string.
- No update fields provided. Use --name or --offbudget.
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/73a0b7f920473fb7.
Report an issue: GitHub.