actualbudget/actual · error · CompileError

Literal not passed to $literal

Error message

Literal not passed to $literal

What it means

The `$literal` function in the AQL compiler requires its argument to have been produced as a literal via `$literal` wrapping when the value was bound, so the compiler can inline it without escaping/inference. If the first argument's compiled state lacks `literal: true`, the compiler throws because `$literal` cannot safely emit the value.

Source

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

      validateArgLength(args, 1);
      return castInput(state, args[0], 'date-year');
    }

    // various functions
    case '$condition':
      validateArgLength(args, 1);
      const conds = compileConditions(state, args[0]);
      return typed(conds.join(' AND '), 'boolean');

    case '$nocase':
      validateArgLength(args, 1);
      const [arg1] = valArray(state, args, ['string']);
      return typed(`${arg1} COLLATE NOCASE`, args[0].type);

    case '$literal': {
      validateArgLength(args, 1);
      if (!args[0].literal) {
        throw new CompileError('Literal not passed to $literal');
      }
      return args[0];
    }
    default:
      throw new CompileError(`Unknown function: ${name}`);
  }
});

const compileOp = saveStack('op', (state, fieldRef, opData) => {
  const { $transform, ...opExpr } = opData;
  const [op] = Object.keys(opExpr);

  const rhs = compileExpr(state, opData[op]);

  let lhs;
  if ($transform) {
    lhs = compileFunction(
      { ...state, implicitField: fieldRef },

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Pass an actual literal value (a plain string/number) to `$literal`, not a `$`-prefixed field reference.
  2. Mark the value as a literal at binding time using the library's literal input mechanism (e.g. `lit()`/raw input helpers) so the compiled arg carries `literal: true`.
  3. If you intended a parameterized value, use the normal `$param`/params mechanism instead of `$literal`.

Example fix

// before
{$literal: '$amount'}
// after
{$literal: 'transactions.amount'}
Defensive patterns

Strategy: validation

Validate before calling

function assertLiteralArg(v) {
  if (typeof v !== 'string' && typeof v !== 'number') {
    throw new Error('$literal requires a raw string/number, not a $-expression');
  }
}

Type guard

const isRawLiteral = (v) => ['string', 'number', 'boolean'].includes(typeof v);

Prevention

When it happens

Trigger: Using `{$literal: someExpr}` where `someExpr` resolves to a normal expression/parameter rather than a value marked with `literal` (e.g. produced from raw JS input compiled as a literal).

Common situations: Trying to inline raw values or fragments into a query without using the proper literal-binding API, or passing a field reference (`$field`) into `$literal` expecting it to be treated as raw.

Related errors


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