usememos/memos · error

NOT expects exactly one argument

Error message

NOT expects exactly one argument

What it means

evaluateBoolExpr constant-folds boolean expressions, including CEL's NOT operator '!_'. It requires exactly one operand; a malformed AST with a different arity (only producible by hand-built or library-generated expressions, since the CEL parser itself enforces unary arity) triggers this error.

Source

Thrown at internal/filter/parser.go:502

		return nil, errors.Errorf("unsupported constant %T", x)
	}
}

func evaluateBool(call *exprv1.Expr_Call) (bool, bool, error) {
	val, ok, err := evaluateBoolExpr(&exprv1.Expr{ExprKind: &exprv1.Expr_CallExpr{CallExpr: call}})
	return val, ok, err
}

func evaluateBoolExpr(expr *exprv1.Expr) (bool, bool, error) {
	if literal, err := getConstValue(expr); err == nil {
		if b, ok := literal.(bool); ok {
			return b, true, nil
		}
		return false, false, nil
	}
	if call := expr.GetCallExpr(); call != nil && call.Function == "!_" {
		if len(call.Args) != 1 {
			return false, false, errors.New("NOT expects exactly one argument")
		}
		val, ok, err := evaluateBoolExpr(call.Args[0])
		if err != nil || !ok {
			return false, false, err
		}
		return !val, true, nil
	}
	return false, false, nil
}

// evaluateNumeric constant-folds an expression to an int64 measured in seconds:
// timestamps and `now` fold to Unix epoch seconds, durations fold to a number of
// seconds, and the two combine through standard arithmetic. CEL has already
// type-checked the operand combinations, so the folded int math is well-formed.
func evaluateNumeric(expr *exprv1.Expr, now time.Time) (int64, bool, error) {
	if literal, err := getConstValue(expr); err == nil {
		switch v := literal.(type) {
		case int64:

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. If building ASTs by hand, give '!_' exactly one argument
  2. Prefer passing filter strings through the normal CEL parser rather than constructing Expr protos directly
  3. Round-trip the AST through cel.Parse of its string form to validate structure

Example fix

// before (hand-built proto)
notCall.Args = nil

// after
notCall.Args = []*exprv1.Expr{operand}
Defensive patterns

Strategy: validation

Validate before calling

// Go (AST builder): enforce unary arity when emitting NOT
if fn == "!_" && len(args) != 1 {
    return errors.New("NOT needs exactly one operand")
}

Prevention

When it happens

Trigger: Programmatically constructing an exprv1.Expr_Call with Function "!_" and zero or multiple args, or a custom CEL AST builder emitting broken NOT nodes. Plain filter strings like !pinned parse correctly and never hit this.

Common situations: Code that synthesizes or transforms CEL ASTs (macros, optimizers) instead of parsing filter text; partial AST rewrites that drop operands.

Related errors


AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15). Data as JSON: /api/errors/a04403ef0fed02d5. Report an issue: GitHub.