{"record":{"id":"19bbde66be3b0669","repo":"usememos/memos","slug":"matches-argument-must-be-a-string","errorCode":null,"errorMessage":"matches argument must be a string","messagePattern":"matches argument must be a string","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/filter/parser.go","lineNumber":370,"sourceCode":"\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.\n\t\tif identName == \"now\" {\n\t\t\treturn &LiteralValue{Value: pc.now.Unix()}, nil\n\t\t}\n\t\tif _, ok := pc.schema.Field(identName); !ok {\n\t\t\treturn nil, errors.Errorf(\"unknown identifier %q\", identName)\n\t\t}\n\t\treturn &FieldRef{Name: identName}, nil\n\t}","sourceCodeStart":352,"sourceCodeEnd":388,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/filter/parser.go#L352-L388","documentation":"After arity checks, the matches() pattern must be a string literal. getConstValue returns the constant but the type switch only accepts string, so numeric, boolean, or identifier arguments fail with this error before a RegexCondition is built.","triggerScenarios":"content.matches(123), content.matches(someVar), or any matches() call whose sole argument is not a quoted string literal.","commonSituations":"Interpolating a precompiled pattern object or unquoted variable into the filter string; forgetting that the filter DSL has no variable binding besides schema fields.","solutions":["Quote the pattern: content.matches(\"^[A-Z]\")","Escape the pattern when embedding it from code (strconv.Quote) to keep it one literal","Remember only literal arguments are supported; there is no dynamic pattern binding"],"exampleFix":"// before\ncontent.matches(123)\n\n// after\ncontent.matches(\"123\")","handlingStrategy":"validation","validationCode":"// Ensure the pattern slot holds a quoted literal before sending\nconst ok = /^\"(?:[^\"\\\\]|\\\\.)*\"$/.test(patternToken);\nif (!ok) throw new Error('matches pattern must be a quoted string literal');","typeGuard":"const isQuotedPattern = (p: string): boolean => /^\"(?:[^\"\\\\]|\\\\.)*\"$/.test(p);","tryCatchPattern":null,"preventionTips":["Embed regex patterns via JSON.stringify to guarantee literal quoting","Do not reference identifiers or numbers as patterns","Test pattern escaping (backslashes) in generated filters"],"tags":["cel","regex","type-mismatch","filter"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}