actualbudget/actual · error · CompileError

Unsupported type of expression:

Error message

Unsupported type of expression: 

What it means

compileLiteral accepts only null, Date, string, boolean, number, and array literals. Anything else (plain objects, functions, symbols, BigInt) throws 'Unsupported type of expression: ' + JSON.stringify(value). The compiler has no SQL representation for it.

Source

Thrown at packages/loot-core/src/server/aql/compiler.ts:481

  } else if (value === null) {
    return typed('NULL', 'null', { literal: true });
  } else if (value instanceof Date) {
    return typed(nativeDateToInt(value), 'date', { literal: true });
  } else if (typeof value === 'string') {
    // Allow user to escape $, and quote the string to make it a
    // string literal in the output
    value = value.replace(/\\\$/g, '$');
    return typed(value, 'string', { literal: true });
  } else if (typeof value === 'boolean') {
    return typed(value ? 1 : 0, 'boolean', { literal: true });
  } else if (typeof value === 'number') {
    return typed(value, Number.isInteger(value) ? 'integer' : 'float', {
      literal: true,
    });
  } else if (Array.isArray(value)) {
    return typed(value, 'array', { literal: true });
  } else {
    throw new CompileError(
      'Unsupported type of expression: ' + JSON.stringify(value),
    );
  }
}

const compileExpr = saveStack('expr', (state, expr) => {
  if (typeof expr === 'string') {
    // Field reference
    if (expr[0] === '$') {
      const fieldRef = expr === '$' ? state.implicitField : expr.slice(1);

      if (fieldRef == null || fieldRef === '') {
        throw new CompileError('Invalid field reference: ' + expr);
      }

      return transformField(state, fieldRef);
    }

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Convert the value to a supported literal: primitive string/number/boolean, null, native Date, or array
  2. Call .toDate() on dayjs/moment wrappers before passing them
  3. Use the proper operator-expression syntax ($gte etc.) instead of a bare object where a literal is expected
  4. Stringify/serialize custom objects yourself before embedding them

Example fix

// before
q.filter({ date: dayjs('2024-01-15') })
// after
q.filter({ date: dayjs('2024-01-15').toDate() })
Defensive patterns

Strategy: type-guard

Validate before calling

const SUPPORTED = ['string','number','boolean'];
function assertLiteral(v) {
  if (v == null || v instanceof Date || Array.isArray(v) || SUPPORTED.includes(typeof v)) return;
  throw new Error('Unsupported query literal: ' + typeof v);
}

Type guard

function isSupportedLiteral(v) {
  return v == null || v instanceof Date || Array.isArray(v) ||
    ['string','number','boolean'].includes(typeof v);
}

Try / catch

try {
  runQuery(query);
} catch (e) {
  if (/Unsupported type of expression/.test(e.message)) {
    console.error('Convert the value to a supported literal (primitive, Date, array, null)');
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing a nested plain object as a literal value (e.g. { date: { gte: '2024-01' } } in a position expecting a raw literal rather than an operator expression); passing a function or class instance; passing Symbol/BigInt.

Common situations: Mixing up operator-object syntax ({ $gte: ... }) with raw literal positions; passing Date-like wrappers (dayjs/moment objects) that aren't real Date instances; serializing errors into query values.

Related errors


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