actualbudget/actual · error · CompileError
Path does not exist:
Error message
Path does not exist:
What it means
For deeply nested paths (3+ segments), makePath (compiler.ts:104) requires that the parent path was already resolved and registered in state.paths. If the parent path (all segments except the last) is missing from the paths map, this CompileError is thrown — joins must be established outer-to-inner.
Source
Thrown at packages/loot-core/src/server/aql/compiler.ts:105
if (!table[field] || table[field].ref == null) {
throw new CompileError(
`Field not joinable on table ${tableName}: "${field}"`,
);
}
return table[field].ref;
}, initialTable);
let joinTable;
const parentParts = parts.slice(0, -1);
if (parentParts.length === 1) {
joinTable = parentParts[0];
} else {
const parentPath = parentParts.join('.');
const parentDesc = paths.get(parentPath);
if (!parentDesc) {
throw new CompileError('Path does not exist: ' + parentPath);
}
joinTable = parentDesc.tableId;
}
return {
tableName,
tableId: uid(tableName),
joinField: parts[parts.length - 1],
joinTable,
};
}
function resolvePath(state, path) {
let paths = path.split('.');
paths = paths.reduce(
(acc, name) => {
const fullName = acc.context + '.' + name;View on GitHub (pinned to d4334cb6e6)
Solutions
- Resolve the full path through normal query APIs (q.select/q.filter with the dotted string) so parents are registered automatically via resolvePath.
- If calling internals, register parent paths in state.paths before the child path.
- Simplify overly deep paths — Actual's schema rarely needs more than two segments; verify the relationship exists.
Example fix
// before (manual, out of order) makePath(state, 'transactions.category.group.id'); // after resolvePath(state, 'transactions.category'); makePath(state, 'transactions.category.group');
Defensive patterns
Strategy: validation
Validate before calling
function assertParentsResolved(state, path) {
const parts = path.split('.');
for (let i = 1; i < parts.length; i++) {
const parent = parts.slice(0, i).join('.');
if (i > 1 && !state.paths.get(parent)) {
throw new Error(`Parent path not resolved: ${parent}`);
}
}
} Type guard
null
Try / catch
try {
makePath(state, deepPath);
} catch (e) {
if (e.message.startsWith('Path does not exist:')) {
// resolve parent paths first via resolvePath, then retry once
resolvePath(state, deepPath.split('.').slice(0, -1).join('.'));
} else throw e;
} Prevention
- Prefer public query-builder APIs which resolve paths in order
- If using internals, always resolve outer segments before inner ones
- Avoid paths deeper than two segments unless the schema supports it
When it happens
Trigger: Requesting a nested path whose parent was never resolved first, e.g. building path info directly via makePath for 'a.b.c' when 'a.b' was never put through resolvePath/makePath; in practice triggered by manually constructing nested field references out of order.
Common situations: Custom tooling calling internal compiler functions directly; constructing query state programmatically and skipping intermediate path registration; unusual nested relationships referenced before their parents in the same expression.
Related errors
- Invalid path: ${path}
- Path error: ${tableName} table does not exist
- Field not joinable on table ${tableName}: "${field}"
- Field "${field}" does not exist in table "${tableName}"
- Invalid field name, must be a string
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/a76527f8f38e042b.
Report an issue: GitHub.