{"record":{"id":"0df8b3fc8b5a069f","repo":"usememos/memos","slug":"contains-argument-must-be-a-string","errorCode":null,"errorMessage":"contains argument must be a string","messagePattern":"contains argument must be a string","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/filter/parser.go","lineNumber":1016,"sourceCode":"\n// buildContainsPredicate extracts the pattern from t.contains(\"substring\").\nfunc buildContainsPredicate(call *exprv1.Expr_Call, iterVar string) (PredicateExpr, error) {\n\tif target := call.Target.GetIdentExpr(); target == nil || target.GetName() != iterVar {\n\t\treturn nil, errors.Errorf(\"contains target must be the iteration variable %q\", iterVar)\n\t}\n\n\tif len(call.Args) != 1 {\n\t\treturn nil, errors.New(\"contains expects exactly one argument\")\n\t}\n\n\tsubstring, err := getConstValue(call.Args[0])\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"contains argument must be a constant string\")\n\t}\n\n\tsubstringStr, ok := substring.(string)\n\tif !ok {\n\t\treturn nil, errors.New(\"contains argument must be a string\")\n\t}\n\n\treturn &ContainsPredicate{Substring: substringStr}, nil\n}\n","sourceCodeStart":998,"sourceCodeEnd":1021,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/filter/parser.go#L998-L1021","documentation":"The argument to contains() in a comprehension predicate must be a compile-time string constant. A constant of another type triggers this error; non-constant arguments surface as the wrapped 'contains argument must be a constant string'.","triggerScenarios":"tag.exists(t, t.contains(42)) or tag.all(t, t.contains(dyn_value)) with a non-string literal — comparing the iterated tag against a numeric/boolean literal.","commonSituations":"Unquoted substrings; users assuming contains accepts any scalar type.","solutions":["Quote the substring: tag.exists(t, t.contains(\"work\"))","Use string literals only; bind dynamic values before the filter or use == with a constant"],"exampleFix":"// before\n`tag.exists(t, t.contains(42))`\n\n// after\n`tag.exists(t, t.contains(\"work\"))`","handlingStrategy":"validation","validationCode":"var badContains = regexp.MustCompile(`contains\\s*\\(\\s*[^\"']`)\n\nfunc validateContains(expr string) error {\n    if badContains.MatchString(expr) {\n        return errors.New(\"contains needs a quoted string substring\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := r.Render(cond); err != nil {\n    if strings.Contains(err.Error(), \"contains argument must be a string\") {\n        return userErr(\"quote the contains substring, e.g. t.contains(\\\"work\\\")\")\n    }\n    return err\n}","preventionTips":["Quote substring literals","Never pass numbers or booleans to contains on string-list fields"],"tags":["cel","filter","parser","comprehension","type-mismatch"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}