usememos/memos · error

comprehension loop step must have two arguments

Error message

comprehension loop step must have two arguments

What it means

For exists()/all(), extractPredicate expects the loop step call (_||_ or _&&_) to have exactly two arguments and reads the predicate from Args[1]. A call with any other arity fails here.

Source

Thrown at internal/filter/parser.go:894

func extractPredicate(comp *exprv1.Expr_Comprehension, _ Schema) (PredicateExpr, error) {
	// The loop step is: @result || predicate(t) for exists
	//                or: @result && predicate(t) for all
	step := comp.LoopStep.GetCallExpr()
	if step == nil {
		return nil, errors.New("comprehension loop step must be a call expression")
	}

	// exists/all: accu || predicate  /  accu && predicate  -> predicate is arg[1].
	// exists_one: predicate ? accu + 1 : accu               -> predicate is arg[0].
	var predicateExpr *exprv1.Expr
	if step.Function == "_?_:_" {
		if len(step.Args) != 3 {
			return nil, errors.New("exists_one loop step must have three arguments")
		}
		predicateExpr = step.Args[0]
	} else {
		if len(step.Args) != 2 {
			return nil, errors.New("comprehension loop step must have two arguments")
		}
		predicateExpr = step.Args[1]
	}
	predicateCall := predicateExpr.GetCallExpr()
	if predicateCall == nil {
		return nil, errors.New("comprehension predicate must be a function call")
	}

	// Handle different predicate functions
	switch predicateCall.Function {
	case "_==_":
		return buildEqualsPredicate(predicateCall, comp.IterVar)
	case "startsWith":
		return buildStartsWithPredicate(predicateCall, comp.IterVar)
	case "endsWith":
		return buildEndsWithPredicate(predicateCall, comp.IterVar)
	case "contains":
		return buildContainsPredicate(predicateCall, comp.IterVar)

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. Use the textual macro form tag.exists(t, predicate) / tag.all(t, predicate)
  2. Emit the canonical two-arg loop step accu || predicate when constructing ASTs manually
Defensive patterns

Strategy: try-catch

Try / catch

if err := r.Render(cond); err != nil {
    if strings.Contains(err.Error(), "loop step must have two arguments") {
        return errors.New("exists/all loop step malformed; use textual macro syntax")
    }
    return err
}

Prevention

When it happens

Trigger: Programmatically built exists/all comprehension where the logical-operator call has fewer or more than 2 args; not reachable from standard textual macro expansion, which always produces accu OP predicate.

Common situations: Custom AST construction; partially-applied or rewritten loop steps from macro transforms.

Related errors


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