actualbudget/actual · error · CompileError
Unknown operator: ${op}
Error message
Unknown operator: ${op} What it means
compileOp dispatches on the filter operator key of an AQL condition (`$eq`, `$like`, `$notlike`, ...) and throws CompileError in its `default` branch when the operator is unrecognized. The query cannot be translated to SQL because the operator has no mapping.
Source
Thrown at packages/loot-core/src/server/aql/compiler.ts:746
`${String(left)} IN (` +
ids.map(id => `'${String(id)}'`).join(',') +
')'
);
}
case '$like': {
const [left, right] = valArray(state, [lhs, rhs], ['string', 'string']);
return `UNICODE_LIKE(${getNormalisedString(right)}, NORMALISE(${left}))`;
}
case '$regexp': {
const [left, right] = valArray(state, [lhs, rhs], ['string', 'string']);
return `REGEXP(${right}, ${left})`;
}
case '$notlike': {
const [left, right] = valArray(state, [lhs, rhs], ['string', 'string']);
return `(NOT UNICODE_LIKE(${getNormalisedString(right)}, NORMALISE(${left}))\n OR ${left} IS NULL)`;
}
default:
throw new CompileError(`Unknown operator: ${op}`);
}
});
function compileConditions(state, conds) {
if (!Array.isArray(conds)) {
// Convert the object form `{foo: 1, bar:2}` into the array form
// `[{foo: 1}, {bar:2}]`
conds = Object.entries(conds).map(cond => {
return { [cond[0]]: cond[1] };
});
}
return conds.filter(Boolean).reduce((res, condsObj) => {
const compiled = Object.entries(condsObj)
.map(([field, cond]) => {
// Allow a falsy value in the lhs of $and and $or to allow for
// quick forms like `$or: amount != 0 && ...`
if (field === '$and') {View on GitHub (pinned to d4334cb6e6)
Solutions
- Check compiler.ts compileOp for the exact supported operator names and correct the key (e.g. `$gte`, `$lt`, `$like`, `$notlike`).
- Replace Mongo-style operators (`$gt`, `$ne`, `$in`) with the AQL equivalents.
- If migrating from an older version, review the changelog for renamed operators.
Example fix
// before
{amount: {$gt: 100}}
// after
{amount: {$gt_: 100}} // or the actual supported operator, e.g. {$gt: 100} -> {$gte: 100} style keys defined in compileOp Defensive patterns
Strategy: validation
Validate before calling
const KNOWN_OPS = new Set(['$eq', '$ne', '$lt', '$lte', '$gt', '$gte', '$like', '$notlike', '$oneof', '$in', ...]);
function assertKnownOp(op) {
if (!KNOWN_OPS.has(op)) throw new Error(`Unknown AQL operator: ${op}`);
} Type guard
const isKnownOp = (op) => typeof op === 'string' && KNOWN_OPS.has(op);
Prevention
- Translate Mongo/SQL operator habits to AQL names once in a helper layer.
- Type filter objects with a union of valid operator keys.
- Test each filter operator against a real query in CI.
When it happens
Trigger: Building a filter like `{amount: {$greaterThan: 100}}` with a misspelled or unsupported operator name, or using an operator that was renamed in a newer AQL version.
Common situations: Typos such as `$gt`/`$>` borrowed from other query languages (Mongo/GraphQL-style operators) that AQL does not support, or docs from an older version listing removed operators.
Related errors
- Table "${tableName}" does not exist in the schema
- Can't cast ${expr.type} to date
- Can't cast ${expr.type} to date-month
- Can't cast ${expr.type} to date-year
- Can't convert ${expr.type} to ${type}
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/7a85f8fd63477d63.
Report an issue: GitHub.