{"record":{"id":"2b7be42e998981cc","repo":"usememos/memos","slug":"equality-predicate-expects-exactly-two-arguments","errorCode":null,"errorMessage":"equality predicate expects exactly two arguments","messagePattern":"equality predicate expects exactly two arguments","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/filter/parser.go","lineNumber":921,"sourceCode":"\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)\n\tdefault:\n\t\treturn nil, errors.Errorf(`unsupported predicate function %q in comprehension (supported: ==, startsWith, endsWith, contains)`, predicateCall.Function)\n\t}\n}\n\n// buildEqualsPredicate extracts the value from t == \"value\".\nfunc buildEqualsPredicate(call *exprv1.Expr_Call, iterVar string) (PredicateExpr, error) {\n\tif len(call.Args) != 2 {\n\t\treturn nil, errors.New(\"equality predicate expects exactly two arguments\")\n\t}\n\n\tvar constExpr *exprv1.Expr\n\tswitch {\n\tcase isIterVarExpr(call.Args[0], iterVar):\n\t\tconstExpr = call.Args[1]\n\tcase isIterVarExpr(call.Args[1], iterVar):\n\t\tconstExpr = call.Args[0]\n\tdefault:\n\t\treturn nil, errors.Errorf(\"equality predicate must compare against the iteration variable %q\", iterVar)\n\t}\n\n\tvalue, err := getConstValue(constExpr)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"equality argument must be a constant string\")\n\t}\n\n\tvalueStr, ok := value.(string)","sourceCodeStart":903,"sourceCodeEnd":939,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/filter/parser.go#L903-L939","documentation":"buildEqualsPredicate translates t == \"value\" inside a comprehension. It requires the == call to have exactly two arguments so it can determine which side is the iteration variable and which side is the constant. Any other arity is rejected.","triggerScenarios":"A programmatically built _==_ call with 0, 1, or 3+ args; textual CEL cannot produce this because == is a binary operator. So in practice this guards against malformed ASTs only.","commonSituations":"Custom macro/AST pipelines; tests constructing Expr_Call nodes directly with wrong arg counts.","solutions":["Use the textual == operator inside the macro: tag.exists(t, t == \"value\")","When building ASTs, always give _==_ exactly two args"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := r.Render(cond); err != nil {\n    if strings.Contains(err.Error(), \"exactly two arguments\") {\n        return errors.New(\"== inside a macro must compare two operands\")\n    }\n    return err\n}","preventionTips":["Use textual == inside macros","Keep binary operator AST nodes two-arg"],"tags":["cel","filter","parser","comprehension","equality"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}