{"record":{"id":"e00170a22f8b4f0a","repo":"usememos/memos","slug":"comprehension-loop-step-must-have-two-arguments","errorCode":null,"errorMessage":"comprehension loop step must have two arguments","messagePattern":"comprehension loop step must have two arguments","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/filter/parser.go","lineNumber":894,"sourceCode":"func extractPredicate(comp *exprv1.Expr_Comprehension, _ Schema) (PredicateExpr, error) {\n\t// The loop step is: @result || predicate(t) for exists\n\t//                or: @result && predicate(t) for all\n\tstep := comp.LoopStep.GetCallExpr()\n\tif step == nil {\n\t\treturn nil, errors.New(\"comprehension loop step must be a call expression\")\n\t}\n\n\t// exists/all: accu || predicate  /  accu && predicate  -> predicate is arg[1].\n\t// exists_one: predicate ? accu + 1 : accu               -> predicate is arg[0].\n\tvar predicateExpr *exprv1.Expr\n\tif step.Function == \"_?_:_\" {\n\t\tif len(step.Args) != 3 {\n\t\t\treturn nil, errors.New(\"exists_one loop step must have three arguments\")\n\t\t}\n\t\tpredicateExpr = step.Args[0]\n\t} else {\n\t\tif len(step.Args) != 2 {\n\t\t\treturn nil, errors.New(\"comprehension loop step must have two arguments\")\n\t\t}\n\t\tpredicateExpr = step.Args[1]\n\t}\n\tpredicateCall := predicateExpr.GetCallExpr()\n\tif predicateCall == nil {\n\t\treturn nil, errors.New(\"comprehension predicate must be a function call\")\n\t}\n\n\t// Handle different predicate functions\n\tswitch predicateCall.Function {\n\tcase \"_==_\":\n\t\treturn buildEqualsPredicate(predicateCall, comp.IterVar)\n\tcase \"startsWith\":\n\t\treturn buildStartsWithPredicate(predicateCall, comp.IterVar)\n\tcase \"endsWith\":\n\t\treturn buildEndsWithPredicate(predicateCall, comp.IterVar)\n\tcase \"contains\":\n\t\treturn buildContainsPredicate(predicateCall, comp.IterVar)","sourceCodeStart":876,"sourceCodeEnd":912,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/filter/parser.go#L876-L912","documentation":"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.","triggerScenarios":"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.","commonSituations":"Custom AST construction; partially-applied or rewritten loop steps from macro transforms.","solutions":["Use the textual macro form tag.exists(t, predicate) / tag.all(t, predicate)","Emit the canonical two-arg loop step accu || predicate when constructing ASTs manually"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := r.Render(cond); err != nil {\n    if strings.Contains(err.Error(), \"loop step must have two arguments\") {\n        return errors.New(\"exists/all loop step malformed; use textual macro syntax\")\n    }\n    return err\n}","preventionTips":["Use textual exists()/all() forms","Keep loop steps as canonical accu OP predicate two-arg calls"],"tags":["cel","filter","parser","comprehension"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}