usememos/memos · error
equality argument must be a string
Error message
equality argument must be a string
What it means
The == predicate inside a comprehension must compare the iteration variable to a compile-time string constant. After locating the constant side, getConstValue extracts its value; if the value exists but is not a string (e.g. int, bool, duration, timestamp), this error is returned. (A non-constant operand instead yields the wrapped 'equality argument must be a constant string'.)
Source
Thrown at internal/filter/parser.go:941
var constExpr *exprv1.Expr
switch {
case isIterVarExpr(call.Args[0], iterVar):
constExpr = call.Args[1]
case isIterVarExpr(call.Args[1], iterVar):
constExpr = call.Args[0]
default:
return nil, errors.Errorf("equality predicate must compare against the iteration variable %q", iterVar)
}
value, err := getConstValue(constExpr)
if err != nil {
return nil, errors.Wrap(err, "equality argument must be a constant string")
}
valueStr, ok := value.(string)
if !ok {
return nil, errors.New("equality argument must be a string")
}
return &EqualsPredicate{Value: valueStr}, nil
}
func isIterVarExpr(expr *exprv1.Expr, iterVar string) bool {
target := expr.GetIdentExpr()
return target != nil && target.GetName() == iterVar
}
// buildStartsWithPredicate extracts the pattern from t.startsWith("prefix").
func buildStartsWithPredicate(call *exprv1.Expr_Call, iterVar string) (PredicateExpr, error) {
// Verify the target is the iteration variable
if target := call.Target.GetIdentExpr(); target == nil || target.GetName() != iterVar {
return nil, errors.Errorf("startsWith target must be the iteration variable %q", iterVar)
}
if len(call.Args) != 1 {View on GitHub (pinned to 14d757ce1f)
Solutions
- Quote the comparison value: tag.exists(t, t == "42")
- Verify the field being iterated is a string-list field (like tag) and use string literals
Example fix
// before `tag.exists(t, t == 42)` // after `tag.exists(t, t == "42")`
Defensive patterns
Strategy: validation
Validate before calling
// Ensure == comparisons inside macros use string literals
var nonStringEq = regexp.MustCompile(`==\s*([^"'\s][^,)]*)`)
func validateEqStrings(expr string) error {
if nonStringEq.MatchString(expr) {
return errors.New("compare tags with double-quoted string literals")
}
return nil
} Try / catch
if err := r.Render(cond); err != nil {
if strings.Contains(err.Error(), "equality argument must be a string") {
return userErr("tag comparisons must use quoted string values")
}
return err
} Prevention
- Always quote values compared against list-of-string fields
- Treat tag names as strings end-to-end, including numeric-looking tags
When it happens
Trigger: tag.exists(t, t == 42) or tag.exists(t, t == true) — comparing the iterated string (tag) to a numeric or boolean literal.
Common situations: Users assume tags can be numbers; copy-pasting filters from other systems where tag lists are typed differently.
Related errors
- startsWith argument must be a string
- endsWith argument must be a string
- contains argument must be a string
- text match argument must be a string
- comprehension accumulator must be initialized with a constan
AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15).
Data as JSON: /api/errors/4834edeaa5624d3e.
Report an issue: GitHub.