usememos/memos · error
tags membership requires string literal
Error message
tags membership requires string literal
What it means
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.
Source
Thrown at internal/filter/render.go:492
}, nil
}
func (r *renderer) renderElementInCondition(cond *ElementInCondition) (renderResult, error) {
field, ok := r.schema.Field(cond.Field)
if !ok {
return renderResult{}, errors.Errorf("unknown field %q", cond.Field)
}
if field.Kind != FieldKindJSONList {
return renderResult{}, errors.Errorf("field %q is not a tag list", cond.Field)
}
lit, err := expectLiteral(cond.Element)
if err != nil {
return renderResult{}, err
}
str, ok := lit.(string)
if !ok {
return renderResult{}, errors.New("tags membership requires string literal")
}
return r.renderJSONListContains(field, str)
}
func (r *renderer) renderJSONListContains(field Field, value string) (renderResult, error) {
return r.renderTagComprehension(field, &EqualsPredicate{Value: value}, ComprehensionExists)
}
func (r *renderer) renderScalarInCondition(field Field, values []ValueExpr) (renderResult, error) {
placeholders := make([]string, 0, len(values))
for _, v := range values {
lit, err := expectLiteral(v)
if err != nil {
return renderResult{}, err
}
switch field.Type {View on GitHub (pinned to 14d757ce1f)
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
Example fix
// before filter := `tag in [work, 1]` // after filter := `tag in ["work", "1"]`
Defensive patterns
Strategy: validation
Validate before calling
// Validate tag-list elements are string literals before submitting a filter
import (
"regexp"
"strings"
)
var nonStringMember = regexp.MustCompile(`(?i)\b(?:tag|tags)\s+(?:in|contains)\s*\[([^\]]*)\]`)
func tagMembersAreStrings(filter string) bool {
for _, m := range nonStringMember.FindAllStringSubmatch(filter, -1) {
for _, part := range strings.Split(m[1], ",") {
p := strings.TrimSpace(part)
if p == "" { continue }
if !strings.HasPrefix(p, "\"") || !strings.HasSuffix(p, "\"") { return false }
}
}
return true
} Try / catch
// Compile the filter during authoring and show the parser error inline
if _, err := engine.CompileToStatement(ctx, filter, opts); err != nil {
return fmt.Errorf("invalid filter: %w", err) // surface 'tags membership requires string literal'
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- filter expression is empty
- filter must evaluate to a boolean value
- invalid use of in operator
- text match expects exactly one argument
- comparison must start with a field reference or supported fu
AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15).
Data as JSON: /api/errors/9bbd8fbae33f420f.
Report an issue: GitHub.