usememos/memos · error
comprehension predicate must be a function call
Error message
comprehension predicate must be a function call
What it means
After extracting the predicate node from the loop step, the parser requires it to be a function call (==, startsWith, endsWith, contains). A predicate that is a bare identifier, constant, select expression, or other node type cannot be translated to a SQL predicate and triggers this error.
Source
Thrown at internal/filter/parser.go:900
}
// exists/all: accu || predicate / accu && predicate -> predicate is arg[1].
// exists_one: predicate ? accu + 1 : accu -> predicate is arg[0].
var predicateExpr *exprv1.Expr
if step.Function == "_?_:_" {
if len(step.Args) != 3 {
return nil, errors.New("exists_one loop step must have three arguments")
}
predicateExpr = step.Args[0]
} else {
if len(step.Args) != 2 {
return nil, errors.New("comprehension loop step must have two arguments")
}
predicateExpr = step.Args[1]
}
predicateCall := predicateExpr.GetCallExpr()
if predicateCall == nil {
return nil, errors.New("comprehension predicate must be a function call")
}
// Handle different predicate functions
switch predicateCall.Function {
case "_==_":
return buildEqualsPredicate(predicateCall, comp.IterVar)
case "startsWith":
return buildStartsWithPredicate(predicateCall, comp.IterVar)
case "endsWith":
return buildEndsWithPredicate(predicateCall, comp.IterVar)
case "contains":
return buildContainsPredicate(predicateCall, comp.IterVar)
default:
return nil, errors.Errorf(`unsupported predicate function %q in comprehension (supported: ==, startsWith, endsWith, contains)`, predicateCall.Function)
}
}
// buildEqualsPredicate extracts the value from t == "value".View on GitHub (pinned to 14d757ce1f)
Solutions
- Write the predicate as an explicit call on the iteration variable, e.g. tag.exists(t, t == "work") or tag.exists(t, t.contains("work"))
- Do not use bare identifiers as predicates; always compare against a string constant
Example fix
// before `tag.exists(t, t)` // after `tag.exists(t, t == "work")`
Defensive patterns
Strategy: validation
Validate before calling
// Require every comprehension predicate to call ==, startsWith, endsWith, or contains
var predFn = regexp.MustCompile(`\.\s*(exists|all|exists_one)\s*\(\s*\w+\s*,\s*([^)]+)\)`)
func validatePredicates(expr string) error {
for _, m := range predFn.FindAllStringSubmatch(expr, -1) {
p := m[2]
ok := strings.Contains(p, "==") || strings.Contains(p, "startsWith") ||
strings.Contains(p, "endsWith") || strings.Contains(p, "contains")
if !ok {
return fmt.Errorf("predicate %q must use ==, startsWith, endsWith, or contains", p)
}
}
return nil
} Try / catch
if err := r.Render(cond); err != nil {
if strings.Contains(err.Error(), "predicate must be a function call") {
return userErr("write the tag predicate as t == \"value\" or t.contains(\"value\")")
}
return err
} Prevention
- Never use a bare identifier as a comprehension predicate
- Always compare the iteration variable to a string constant
When it happens
Trigger: tag.exists(t, t) (bare iteration variable), tag.all(t, someFlag) where someFlag is an ident, or any predicate not built from one of the four supported operators.
Common situations: Users write tag.exists(t, t) expecting truthiness filtering, or reference a boolean property of the iterated element instead of comparing it to a constant.
Related errors
- comprehension accumulator must be initialized with a constan
- unsupported comprehension type (supported: exists, all, exis
- comprehension loop step must be a call expression
- exists_one loop step must have three arguments
- comprehension loop step must have two arguments
AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15).
Data as JSON: /api/errors/2aff3771e772178f.
Report an issue: GitHub.