{"record":{"id":"298be120adc9fc14","repo":"usememos/memos","slug":"matches-expects-exactly-one-argument","errorCode":null,"errorMessage":"matches expects exactly one argument","messagePattern":"matches expects exactly one argument","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/filter/parser.go","lineNumber":362,"sourceCode":"\nfunc buildMatchesCondition(call *exprv1.Expr_Call, schema Schema) (Condition, error) {\n\tif call.Target == nil {\n\t\treturn nil, errors.New(\"matches requires a target\")\n\t}\n\ttargetName, err := getIdentName(call.Target)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfield, ok := schema.Field(targetName)\n\tif !ok {\n\t\treturn nil, errors.Errorf(\"unknown identifier %q\", targetName)\n\t}\n\tif !field.SupportsContains {\n\t\treturn nil, errors.Errorf(\"identifier %q does not support matches()\", targetName)\n\t}\n\tif len(call.Args) != 1 {\n\t\treturn nil, errors.New(\"matches expects exactly one argument\")\n\t}\n\tvalue, err := getConstValue(call.Args[0])\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"matches only supports literal arguments\")\n\t}\n\tpattern, ok := value.(string)\n\tif !ok {\n\t\treturn nil, errors.New(\"matches argument must be a string\")\n\t}\n\treturn &RegexCondition{\n\t\tField:   targetName,\n\t\tPattern: pattern,\n\t}, nil\n}\n\nfunc buildValueExpr(expr *exprv1.Expr, pc parseContext) (ValueExpr, error) {\n\tif identName, err := getIdentName(expr); err == nil {\n\t\t// `now` is not a schema field; it folds to the frozen evaluation time.","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/filter/parser.go#L344-L380","documentation":"The regex matches() translation accepts exactly one argument (the pattern). A call with zero or 2+ arguments cannot be compiled to a single SQL regex predicate, so buildMatchesCondition rejects it at parse time.","triggerScenarios":"content.matches() or content.matches(\"a\", \"b\") in a filter expression passed to the memo filter API.","commonSituations":"Copy-paste from JS RegExp(text, flags) style where flags are a second argument; hand-written filters missing the pattern string.","solutions":["Pass exactly one regex pattern: content.matches(\"^TODO.*\")","Drop any flags argument; use inline (?i) in the pattern for case-insensitive matching if supported by the store's regex dialect","Lint user-supplied filters with a CEL parser before calling the API"],"exampleFix":"// before\ncontent.matches(\"todo\", \"i\")\n\n// after\ncontent.matches(\"(?i)todo\")","handlingStrategy":"validation","validationCode":"// Go: quick arity lint for matches\nre := regexp.MustCompile(`\\.matches\\((?:[^\"()]|\"[^\"]*\")*\\)`)\n// full validation is easier via cel.Parse + an Ast walker asserting len(args)==1","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass one pattern string per matches() call","Use inline flags like (?i) instead of a second argument","Dry-run compile filters in CI for template-generated expressions"],"tags":["cel","regex","arity","filter"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}