usememos/memos · error
expression must be a literal
Error message
expression must be a literal
What it means
A generic guard in the filter renderer (expectLiteral) that rejects any value expression which is not a *LiteralValue node. The SQL rendering path can only bind concrete constant values; references, function calls, or arithmetic cannot be translated to placeholders. Callers that wrap it (tag membership, boolean checks, numeric limits) surface this raw message.
Source
Thrown at internal/filter/render.go:850
func (r *renderer) addBoolArg(value bool) string {
var v any
switch r.dialect {
case DialectSQLite:
if value {
v = 1
} else {
v = 0
}
default:
v = value
}
return r.addArg(v)
}
func expectLiteral(expr ValueExpr) (any, error) {
lit, ok := expr.(*LiteralValue)
if !ok {
return nil, errors.New("expression must be a literal")
}
return lit.Value, nil
}
func expectBool(expr ValueExpr) (bool, error) {
lit, err := expectLiteral(expr)
if err != nil {
return false, err
}
value, ok := lit.(bool)
if !ok {
return false, errors.New("boolean literal required")
}
return value, nil
}
func expectNumericLiteral(expr ValueExpr) (int64, error) {
lit, err := expectLiteral(expr)View on GitHub (pinned to 14d757ce1f)
Solutions
- Replace the right-hand side with a literal constant (string in quotes, number, true/false)
- If generating filters in code, interpolate evaluated values as literals, not raw symbols
- Check the filter grammar docs for which operators accept literals only
Example fix
// before engine.CompileToStatement(ctx, "tag in [tag_name]", opts) // after engine.CompileToStatement(ctx, "tag in [\"tag_name\"]", opts)
Defensive patterns
Strategy: validation
Validate before calling
// Reject filters whose RHS of ==/in contains a bare identifier (non-literal)
var bareIdentRHS = regexp.MustCompile(`==\s*[A-Za-z_][A-Za-z0-9_]*\s*$`)
func hasBareIdentifierRHS(filter string) bool { return bareIdentRHS.MatchString(filter) } Try / catch
// Treat compile errors as user input errors (400), not server faults
if _, err := engine.CompileToStatement(ctx, filter, opts); err != nil {
return status.Errorf(codes.InvalidArgument, "invalid filter: %v", err)
} Prevention
- Only use literal constants on the right-hand side of comparisons
- Evaluate dynamic values client-side and inline them as literals
- Validate filters with a compile step before saving
When it happens
Trigger: Compiling a filter where a position requiring a constant gets a non-literal AST node, e.g. `limit in [row_count]`, `pinned == NOT true`, or any identifier/expression used where expectLiteral/expectBool/expectNumericLiteral is invoked.
Common situations: Hand-written filter strings saved in memo queries or API calls that reference fields or functions on the right-hand side; filters generated programmatically that forget to stringify/quote values before serialization.
Related errors
- filter expression is empty
- filter must evaluate to a boolean value
- invalid use of in operator
- text match expects exactly one argument
- tags membership requires string literal
AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15).
Data as JSON: /api/errors/72e654c2bae1f1bf.
Report an issue: GitHub.