actualbudget/actual · error · CompileError
Invalid path: ${path}
Error message
Invalid path: ${path} What it means
makePath (compiler.ts:71) resolves a dotted relationship path used by AQL field references. A valid path must contain at least a table and a field (e.g. 'payee.name'). If the path has fewer than two dot-separated parts, the compiler cannot form a join and throws this CompileError.
Source
Thrown at packages/loot-core/src/server/aql/compiler.ts:76
if (schema[tableName] == null) {
throw new CompileError(`Table "${tableName}" does not exist in the schema`);
}
const fieldDesc = schema[tableName][field];
if (fieldDesc == null) {
throw new CompileError(
`Field "${field}" does not exist in table "${tableName}"`,
);
}
return fieldDesc;
}
function makePath(state, path) {
const { schema, paths } = state;
const parts = path.split('.');
if (parts.length < 2) {
throw new CompileError('Invalid path: ' + path);
}
const initialTable = parts[0];
const tableName = parts.slice(1).reduce((tableName, field) => {
const table = schema[tableName];
if (table == null) {
throw new CompileError(`Path error: ${tableName} table does not exist`);
}
if (!table[field] || table[field].ref == null) {
throw new CompileError(
`Field not joinable on table ${tableName}: "${field}"`,
);
}
return table[field].ref;View on GitHub (pinned to d4334cb6e6)
Solutions
- Append the field name to the path: use 'payee.name' instead of 'payee'.
- If you want the raw id column of the implicit table, use an unqualified field like 'id' (no path) rather than a one-part path.
- Inspect the dynamic code that builds the field string and ensure it always produces at least 'table.field'.
Example fix
// before
q('transactions').select('payee')
// after
q('transactions').select('payee.name') Defensive patterns
Strategy: validation
Validate before calling
function assertPath(path) {
if (typeof path !== 'string' || path.split('.').length < 2) {
throw new Error(`Field path must be "table.field": got ${path}`);
}
}
assertPath('payee.name'); Type guard
function isFieldPath(v) {
return typeof v === 'string' && /^[A-Za-z_][\w]*\.[A-Za-z_][\w]*$/.test(v);
} Try / catch
try {
runQuery(q);
} catch (e) {
if (String(e.message).startsWith('Invalid path:')) {
console.warn('Dotted field path required, e.g. payee.name');
} else throw e;
} Prevention
- Always write relationship references as relation.field
- Validate dynamically built field strings end with a field segment
- Add unit tests for query builders that interpolate field names
When it happens
Trigger: Calling resolvePath/transformField with a bare table name such as q('transactions').select('payee') or filter({ 'account': ... }) — a single segment with no '.field' part.
Common situations: Passing a relationship/table name instead of 'relation.field'; dynamically building field strings and ending up with just the prefix; copy-pasting a path and dropping the '.field' suffix; rules or reports configured with an incomplete field path.
Related errors
- Path error: ${tableName} table does not exist
- Field not joinable on table ${tableName}: "${field}"
- Path does not exist:
- 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/70128d48d3a8589e.
Report an issue: GitHub.