{"record":{"id":"fa375933b47e9860","repo":"usememos/memos","slug":"in-operator-requires-a-field-on-the-left-hand-side","errorCode":null,"errorMessage":"IN operator requires a field on the left-hand side","messagePattern":"IN operator requires a field on the left-hand side","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/filter/render.go","lineNumber":423,"sourceCode":"\t\t\tboolStr = \"true\"\n\t\t}\n\t\treturn renderResult{\n\t\t\tsql: fmt.Sprintf(\"%s %s CAST('%s' AS JSON)\", jsonExpr, sqlOperator(op), boolStr),\n\t\t}, nil\n\tcase DialectPostgres:\n\t\tplaceholder := r.addArg(value)\n\t\treturn renderResult{\n\t\t\tsql: fmt.Sprintf(\"(%s)::boolean %s %s\", jsonExpr, sqlOperator(op), placeholder),\n\t\t}, nil\n\tdefault:\n\t\treturn renderResult{}, errors.Errorf(\"unsupported dialect %s\", r.dialect)\n\t}\n}\n\nfunc (r *renderer) renderInCondition(cond *InCondition) (renderResult, error) {\n\tfieldRef, ok := cond.Left.(*FieldRef)\n\tif !ok {\n\t\treturn renderResult{}, errors.New(\"IN operator requires a field on the left-hand side\")\n\t}\n\n\tif fieldRef.Name == \"tag\" {\n\t\treturn r.renderTagInList(cond.Values)\n\t}\n\n\tfield, ok := r.schema.Field(fieldRef.Name)\n\tif !ok {\n\t\treturn renderResult{}, errors.Errorf(\"unknown field %q\", fieldRef.Name)\n\t}\n\n\tif field.Kind != FieldKindScalar {\n\t\treturn renderResult{}, errors.Errorf(\"field %q does not support IN()\", fieldRef.Name)\n\t}\n\n\treturn r.renderScalarInCondition(field, cond.Values)\n}\n","sourceCodeStart":405,"sourceCodeEnd":441,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/filter/render.go#L405-L441","documentation":"renderInCondition requires the left-hand side of an IN comparison to be a plain field reference so it can resolve the column. A literal, function call, or accessor on the left (e.g. \"work\" in tag or content.size() in [1,2]) cannot be translated and fails here.","triggerScenarios":"Reversed IN expressions like `\"work\" in tag`, or programmatic InCondition construction where Left is not a *FieldRef.","commonSituations":"Users mirroring SQL habit ('value IN column') instead of CEL's field-first syntax; API clients building InCondition objects directly.","solutions":["Write IN with the field first: tag in [\"work\", \"draft\"]","When constructing InCondition in Go, set Left to a *filter.FieldRef"],"exampleFix":"// before\n`\"work\" in tag`\n\n// after\n`tag in [\"work\"]`","handlingStrategy":"type-guard","validationCode":"if _, ok := inCond.Left.(*filter.FieldRef); !ok {\n    return errors.New(\"IN needs a field on the left\")\n}","typeGuard":"func isInRenderable(c *filter.InCondition) bool {\n    _, ok := c.Left.(*filter.FieldRef)\n    return ok\n}","tryCatchPattern":"if err := r.Render(cond); err != nil {\n    if strings.Contains(err.Error(), \"IN operator requires a field\") {\n        return userErr(\"write tag in [\\\"work\\\"] with the field first\")\n    }\n    return err\n}","preventionTips":["Use CEL syntax field in [list], not SQL-style value in column","Set InCondition.Left to a *FieldRef when building conditions in Go"],"tags":["cel","filter","renderer","sql","in-operator"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}