{"record":{"id":"20026d69a042e7ec","repo":"usememos/memos","slug":"size-expects-one-argument","errorCode":null,"errorMessage":"size() expects one argument","messagePattern":"size\\(\\) expects one argument","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/filter/parser.go","lineNumber":413,"sourceCode":"\t\treturn nil, err\n\t} else if ok {\n\t\treturn &LiteralValue{Value: value}, nil\n\t}\n\n\tif boolVal, ok, err := evaluateBoolExpr(expr); err != nil {\n\t\treturn nil, err\n\t} else if ok {\n\t\treturn &LiteralValue{Value: boolVal}, nil\n\t}\n\n\tif call := expr.GetCallExpr(); call != nil {\n\t\tif call.Target != nil && isTimestampAccessor(call.Function) {\n\t\t\treturn buildTimestampAccessor(call, pc)\n\t\t}\n\t\tswitch call.Function {\n\t\tcase \"size\":\n\t\t\tif len(call.Args) != 1 {\n\t\t\t\treturn nil, errors.New(\"size() expects one argument\")\n\t\t\t}\n\t\t\targ, err := buildValueExpr(call.Args[0], pc)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn &FunctionValue{\n\t\t\t\tName: \"size\",\n\t\t\t\tArgs: []ValueExpr{arg},\n\t\t\t}, nil\n\t\tcase \"_+_\", \"_-_\", \"_*_\":\n\t\t\tvalue, ok, err := evaluateNumeric(expr, pc.now)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif ok {\n\t\t\t\treturn &LiteralValue{Value: value}, nil\n\t\t\t}\n\t\tdefault:","sourceCodeStart":395,"sourceCodeEnd":431,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/filter/parser.go#L395-L431","documentation":"The size() function inside a filter value expression (e.g. comparing tag list lengths) must take exactly one argument. buildValueExpr's switch on call.Function builds a FunctionValue{Name:\"size\"} with a single compiled argument; any other arity is rejected because there is no SQL translation for it.","triggerScenarios":"Filters like size() == 0 or size(tag, visibility) > 1 — the parser sees len(call.Args) != 1 inside the value-expression path and errors out.","commonSituations":"Assuming size() with no args returns a default field's size; porting SQL-ish count(col1, col2) habits into the CEL filter.","solutions":["Pass one list field: size(tag) == 0","Combine multiple size comparisons with && instead of extra arguments","Check the schema for which fields are JSON lists before using size()"],"exampleFix":"// before\nsize() == 0\n\n// after\nsize(tag) == 0","handlingStrategy":"validation","validationCode":"// TS: before submitting, assert every size( has exactly one field arg\nconst bad = [...filter.matchAll(/size\\(([^)]*)\\)/g)].some(m => m[1].split(',').length !== 1 || m[1].trim() === '');\nif (bad) throw new Error('size() takes exactly one list field');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use size(field) with one JSON-list field only","Combine multiple size conditions with &&","Consult the field schema for list-typed fields before using size()"],"tags":["cel","filter","arity","lists"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}