{"record":{"id":"16494c8c36178d96","repo":"usememos/memos","slug":"exists-one-loop-step-must-have-three-arguments","errorCode":null,"errorMessage":"exists_one loop step must have three arguments","messagePattern":"exists_one loop step must have three arguments","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/filter/parser.go","lineNumber":889,"sourceCode":"\n\treturn \"\", errors.New(\"unsupported comprehension type (supported: exists, all, exists_one)\")\n}\n\n// extractPredicate extracts the predicate expression from the comprehension loop step.\nfunc 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\":","sourceCodeStart":871,"sourceCodeEnd":907,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/filter/parser.go#L871-L907","documentation":"For exists_one(), the parser expects the loop step to be the ternary call _?_:_ with exactly three arguments (predicate, accu+1, accu). If the args slice has any other length, this error is thrown before the predicate is read from Args[0].","triggerScenarios":"A hand-built or rewritten exists_one comprehension whose _?_:_ call does not carry 3 args; practically only reachable from programmatic AST construction or a non-standard CEL macro expansion, since textual exists_one() always expands to a 3-arg ternary.","commonSituations":"Custom macros registered in a cel-go environment that emit _?_:_ with partial args; AST transformations between parse and filter conversion.","solutions":["Use textual exists_one() in the filter, e.g. tag.exists_one(t, t == \"work\")","If building the AST yourself, emit the canonical expansion: LoopStep = ternary(predicate, accu+1, accu) with all three args"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := r.Render(cond); err != nil {\n    if strings.Contains(err.Error(), \"exists_one loop step\") {\n        // malformed AST; regenerate from textual exists_one()\n        return errors.New(\"exists_one must be written as exists_one(x, predicate)\")\n    }\n    return err\n}","preventionTips":["Never hand-construct the _?_:_ loop step; use textual exists_one()","Test programmatic AST builders against known-good textual expansions"],"tags":["cel","filter","parser","comprehension","exists-one"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}