actualbudget/actual · error · CompileError
Invalid internal table filter: only object filters are suppo
Error message
Invalid internal table filter: only object filters are supported
What it means
When AQL queries internal tables, `tableFilters` supplies implicit filters for that table. These filters must be simple `{field: value}` objects because they are compiled without join support; if a filter is an array (array-form condition), CompileError is thrown since array filters may reference joins that internal table filters cannot handle.
Source
Thrown at packages/loot-core/src/server/aql/compiler.ts:1094
export function compileQuery(
queryState: QueryState,
schema: Schema,
schemaConfig: SchemaConfig = {},
) {
const { withDead, validateRefs = true, tableOptions, rawMode } = queryState;
const {
tableViews = {},
tableFilters = () => [],
customizeQuery = queryState => queryState,
} = schemaConfig;
const internalTableFilters = name => {
const filters = tableFilters(name);
// These filters cannot join tables and must be simple strings
for (const filter of filters) {
if (Array.isArray(filter)) {
throw new CompileError(
'Invalid internal table filter: only object filters are supported',
);
}
if (Object.keys(filter)[0].indexOf('.') !== -1) {
throw new CompileError(
'Invalid internal table filter: field names cannot contain paths',
);
}
}
return filters;
};
const tableRef = (name: string, isJoin?: boolean) => {
const view =
typeof tableViews === 'function'
? tableViews(name, { withDead, isJoin, tableOptions })
: tableViews[name];
return view || name;View on GitHub (pinned to d4334cb6e6)
Solutions
- Rewrite the filter as a plain object: `{field: value}` or `{field: {$op: value}}`.
- Move complex join-dependent conditions out of tableFilters into the query itself.
- Add a preprocessing step that converts array-form conditions to object form before registering table filters.
Example fix
// before
tableFilters = { tombstone: ['tombstone', '=', 0] }
// after
tableFilters = { tombstone: { tombstone: 0 } } Defensive patterns
Strategy: validation
Validate before calling
function assertObjectFilters(filters) {
for (const f of filters) {
if (Array.isArray(f)) throw new Error('Internal table filters must be object form');
}
} Type guard
const isObjectFilter = (f) => typeof f === 'object' && f !== null && !Array.isArray(f);
Prevention
- Always write tableFilters entries in {field: value} object form.
- Keep join-dependent conditions in the main query, not tableFilters.
- Add a unit test for any custom tableFilters mapping.
When it happens
Trigger: Providing a custom `tableFilters` mapping for an internal table where a filter is written in the array/condition-list form `[['amount', '$gt', 5]]` instead of the object form `{amount: 5}`.
Common situations: Extending the AQL schema with views/dead-keys filters and reusing condition arrays from elsewhere, or constructing filters programmatically and mixing the two accepted condition forms.
Related errors
- Invalid internal table filter: field names cannot contain pa
- Table "${tableName}" does not exist in the schema
- Field "${field}" does not exist in table "${tableName}"
- Path error: ${tableName} table does not exist
- Table "${pathInfo.tableName}" does not exist
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/a849ab431462be7a.
Report an issue: GitHub.