{"record":{"id":"6a907cffb63f72a4","repo":"usememos/memos","slug":"matches-requires-a-target","errorCode":null,"errorMessage":"matches requires a target","messagePattern":"matches requires a target","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/filter/parser.go","lineNumber":347,"sourceCode":"\t}\n\tvalue, err := getConstValue(call.Args[0])\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"text match only supports literal arguments\")\n\t}\n\tstr, ok := value.(string)\n\tif !ok {\n\t\treturn nil, errors.New(\"text match argument must be a string\")\n\t}\n\treturn &TextMatchCondition{\n\t\tField: targetName,\n\t\tMode:  mode,\n\t\tValue: str,\n\t}, nil\n}\n\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 {","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/filter/parser.go#L329-L365","documentation":"buildMatchesCondition compiles CEL's matches() (regex match) and requires a receiver target, i.e. field.matches(pattern). The CEL AST for a global function call with no target has call.Target == nil, and the parser has no way to know which column to match, so it fails immediately.","triggerScenarios":"A filter that calls matches as a global function: matches(\"^TODO\") instead of the receiver form content.matches(\"^TODO\"). This typically happens when filter strings are assembled incorrectly or a macro/variable substitution drops the field prefix.","commonSituations":"Template-based filter builders that prepend/append fragments and accidentally emit matches() without its receiver; converting a contains() call and forgetting to keep the field.","solutions":["Use the receiver form: content.matches(\"^TODO\")","Check filter templates for accidental deletion of the field name before the dot","Pre-parse the CEL expression to confirm the call has a target before submitting"],"exampleFix":"// before\nmatches(\"^TODO\")\n\n// after\ncontent.matches(\"^TODO\")","handlingStrategy":"validation","validationCode":"// Reject global-style matches() before submission\nif (/(^|[\\s&|(])matches\\s*\\(/.test(filter)) {\n  throw new Error('matches() must be called on a field: field.matches(\"pattern\")');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always write matches() in receiver form: field.matches(\"pattern\")","When templating filters, lint that every function call has a dotted receiver","Document the receiver form prominently in filter help text"],"tags":["cel","regex","filter","parser"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}