{"record":{"id":"9bbd8fbae33f420f","repo":"usememos/memos","slug":"tags-membership-requires-string-literal","errorCode":null,"errorMessage":"tags membership requires string literal","messagePattern":"tags membership requires string literal","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/filter/render.go","lineNumber":492,"sourceCode":"\t}, nil\n}\n\nfunc (r *renderer) renderElementInCondition(cond *ElementInCondition) (renderResult, error) {\n\tfield, ok := r.schema.Field(cond.Field)\n\tif !ok {\n\t\treturn renderResult{}, errors.Errorf(\"unknown field %q\", cond.Field)\n\t}\n\tif field.Kind != FieldKindJSONList {\n\t\treturn renderResult{}, errors.Errorf(\"field %q is not a tag list\", cond.Field)\n\t}\n\n\tlit, err := expectLiteral(cond.Element)\n\tif err != nil {\n\t\treturn renderResult{}, err\n\t}\n\tstr, ok := lit.(string)\n\tif !ok {\n\t\treturn renderResult{}, errors.New(\"tags membership requires string literal\")\n\t}\n\n\treturn r.renderJSONListContains(field, str)\n}\n\nfunc (r *renderer) renderJSONListContains(field Field, value string) (renderResult, error) {\n\treturn r.renderTagComprehension(field, &EqualsPredicate{Value: value}, ComprehensionExists)\n}\n\nfunc (r *renderer) renderScalarInCondition(field Field, values []ValueExpr) (renderResult, error) {\n\tplaceholders := make([]string, 0, len(values))\n\n\tfor _, v := range values {\n\t\tlit, err := expectLiteral(v)\n\t\tif err != nil {\n\t\t\treturn renderResult{}, err\n\t\t}\n\t\tswitch field.Type {","sourceCodeStart":474,"sourceCodeEnd":510,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/filter/render.go#L474-L510","documentation":"Thrown by the memo filter compiler (internal/filter/render.go) when a tag-membership expression like `tag in [...]` or `tag CONTAINS <value>` uses a non-string literal element. The renderer must embed the tag value into a SQL comprehension over a JSON list column, so only string literals are supported. Any variable, numeric literal, or nested expression as the element triggers this error.","triggerScenarios":"A CEL/SQL-like filter string such as `tag in [1, 2]`, `tag in [true]`, or `content.tag == some_expr` compiled via the filter engine's tag-list rendering path (renderJSONListContains -> expectLiteral -> type assertion to string fails).","commonSituations":"Users writing saved filters with unquoted tag names (`tag in [work]` parsed as identifier instead of `\"work\"`), numeric tag IDs, or copying filters from a different filter dialect that permits non-string members.","solutions":["Quote the tag values: `tag in [\"work\", \"personal\"]` instead of `tag in [work]` or `tag in [1]`","Verify the field you reference is actually a tag-list field (FieldKindJSONList); scalar fields use different operators","If you need dynamic values, resolve them to concrete string literals before building the filter string"],"exampleFix":"// before\nfilter := `tag in [work, 1]`\n// after\nfilter := `tag in [\"work\", \"1\"]`","handlingStrategy":"validation","validationCode":"// Validate tag-list elements are string literals before submitting a filter\nimport (\n  \"regexp\"\n  \"strings\"\n)\nvar nonStringMember = regexp.MustCompile(`(?i)\\b(?:tag|tags)\\s+(?:in|contains)\\s*\\[([^\\]]*)\\]`)\nfunc tagMembersAreStrings(filter string) bool {\n  for _, m := range nonStringMember.FindAllStringSubmatch(filter, -1) {\n    for _, part := range strings.Split(m[1], \",\") {\n      p := strings.TrimSpace(part)\n      if p == \"\" { continue }\n      if !strings.HasPrefix(p, \"\\\"\") || !strings.HasSuffix(p, \"\\\"\") { return false }\n    }\n  }\n  return true\n}","typeGuard":null,"tryCatchPattern":"// Compile the filter during authoring and show the parser error inline\nif _, err := engine.CompileToStatement(ctx, filter, opts); err != nil {\n  return fmt.Errorf(\"invalid filter: %w\", err) // surface 'tags membership requires string literal'\n}","preventionTips":["Quote all tag values in filter strings","Compile filters with the same engine before persisting them","Build filters via helper functions that always quote tag values"],"tags":["filter","validation","cel","sql"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}