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
- Pass an actual literal value (a plain string/number) to `$literal`, not a `$`-prefixed field reference.
- 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`.
- 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
- Only pass raw JS values to $literal, never $-prefixed field refs.
- Use parameter binding ($param) for dynamic values instead of $literal.
- Document $literal as an escape hatch for raw SQL fragments.
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
- 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/c55c62534cbed850.
Report an issue: GitHub.