actualbudget/actual · error · CompileError
A non-literal null doesn't make sense
Error message
A non-literal null doesn't make sense
What it means
In castInput (compiler.ts:252), a null expression is only meaningful as an explicit literal (e.g. from the $lightning/$null literal or null passed as a literal value). A non-literal 'null' AST node has no column/parameter backing, so the compiler refuses it with this CompileError.
Source
Thrown at packages/loot-core/src/server/aql/compiler.ts:254
) {
throw new Error(
`Parameter "${param.paramName}" can't convert to ${type} (already inferred as ${existingType})`,
);
}
} else {
param.paramType = type;
}
}
function castInput(state, expr, type) {
if (expr.type === type) {
return expr;
} else if (expr.type === 'param') {
inferParam(expr, type);
return typed(expr.value, type);
} else if (expr.type === 'null') {
if (!expr.literal) {
throw new CompileError("A non-literal null doesn't make sense");
}
if (type === 'boolean') {
return typed(0, 'boolean', { literal: true });
}
return expr;
}
// These are all things that can be safely casted automatically
if (type === 'date') {
if (expr.type === 'string') {
if (expr.literal) {
return parseDate(expr.value) || badDateFormat(expr.value, 'date');
} else {
throw new CompileError(
'Casting string fields to dates is not supported',
);
}View on GitHub (pinned to d4334cb6e6)
Solutions
- Pass the JavaScript value null through the public query builder (e.g. filter({ payee_id: null })) instead of hand-building the AST node.
- If you must build the node, set literal: true on the null expression.
- Replace hand-built null comparisons with the appropriate query-builder operators.
Example fix
// before
{ type: 'null', literal: false }
// after
{ type: 'null', literal: true } // or use q/filter with null directly Defensive patterns
Strategy: validation
Validate before calling
function assertLiteralNull(node) {
if (node && node.type === 'null' && node.literal !== true) {
throw new Error('Null expressions must be literal; pass null via the query builder instead');
}
} Type guard
function isLiteralNull(node) {
return node != null && typeof node === 'object' && node.type === 'null' && node.literal === true;
} Try / catch
try {
runCompiledExpr(expr);
} catch (e) {
if (e.message.includes("non-literal null")) {
// rebuild expression via public API with plain null value
} else throw e;
} Prevention
- Use the public query builder (filter with null) instead of hand-built AST nodes
- If building ASTs manually, always set literal: true on null nodes
- Keep expression-construction code in one tested helper
When it happens
Trigger: Constructing an expression AST by hand with { type: 'null', literal: false } and feeding it through val/compileOp/compileFunction; using internal expression-builder APIs in a way that produces an unmarked null node.
Common situations: Custom query-building code that creates AST nodes directly instead of using the public query builder; older internal tooling producing null nodes without the literal flag after compiler changes.
Related errors
- Field "${field}" does not exist in table "${tableName}"
- Invalid path: ${path}
- Path error: ${tableName} table does not exist
- Field not joinable on table ${tableName}: "${field}"
- Path does not exist:
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/4901f3bbac0b84ec.
Report an issue: GitHub.