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

  1. Check compiler.ts compileOp for the exact supported operator names and correct the key (e.g. `$gte`, `$lt`, `$like`, `$notlike`).
  2. Replace Mongo-style operators (`$gt`, `$ne`, `$in`) with the AQL equivalents.
  3. 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

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


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/7a85f8fd63477d63. Report an issue: GitHub.