{"record":{"id":"5caa3de2b1acad75","repo":"usememos/memos","slug":"invalidargument-5caa3d","errorCode":"InvalidArgument","errorMessage":"empty expression","messagePattern":"empty expression","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"server/router/api/v1/user_service_filter.go","lineNumber":54,"sourceCode":"\t// Parse and check the filter expression\n\tcelAST, issues := env.Compile(filterStr)\n\tif issues != nil && issues.Err() != nil {\n\t\treturn \"\", errors.Wrapf(issues.Err(), \"invalid filter expression: %s\", filterStr)\n\t}\n\n\t// Extract username from the AST\n\tusername, err := extractUsernameFromAST(celAST.NativeRep().Expr())\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn username, nil\n}\n\n// extractUsernameFromAST extracts the username value from a CEL AST expression.\nfunc extractUsernameFromAST(expr ast.Expr) (string, error) {\n\tif expr == nil {\n\t\treturn \"\", errors.New(\"empty expression\")\n\t}\n\n\t// Check if this is a call expression (for ==, !=, etc.)\n\tif expr.Kind() != ast.CallKind {\n\t\treturn \"\", errors.New(\"filter must be a comparison expression (e.g., username == 'value')\")\n\t}\n\n\tcall := expr.AsCall()\n\n\t// We only support == operator\n\tif call.FunctionName() != \"_==_\" {\n\t\treturn \"\", errors.Errorf(\"unsupported operator: %s (only '==' is supported)\", call.FunctionName())\n\t}\n\n\t// The call should have exactly 2 arguments\n\targs := call.Args()\n\tif len(args) != 2 {\n\t\treturn \"\", errors.New(\"invalid comparison expression\")","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/server/router/api/v1/user_service_filter.go#L36-L72","documentation":"In the ListUsers CEL filter path, the parsed AST's root expression is nil, which happens when the CEL library produced an empty AST from an empty/whitespace filter string. extractUsernameFromAST guards the nil root before inspecting kinds. Returned as InvalidArgument.","triggerScenarios":"ListUsers(filter='') or filter='   '; a client defaulting the filter field to empty string; a filter builder that joins zero conditions into ''.","commonSituations":"Optional search box on a user list page serializing as empty string instead of being omitted; admin tooling passing an env-provided filter that is unset.","solutions":["Omit the filter field entirely when no username filtering is wanted.","Client-side guard: if (!filter.trim()) delete request.filter.","If a 'list all' behavior is needed from a variable, branch the call instead of sending an empty filter."],"exampleFix":"// before\nconst users = await userClient.listUsers({ filter }); // filter = ''\n\n// after\nconst req = {};\nif (filter.trim()) req.filter = filter; // e.g. \"username == 'alice'\"\nconst users = await userClient.listUsers(req);","handlingStrategy":"validation","validationCode":"if (filter !== undefined && !filter.trim()) {\n  throw new Error('filter must be non-empty when provided');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Omit the filter field for unfiltered listings.","Delete optional string fields when blank during request construction.","Remember this endpoint's filter supports only username == 'value'."],"tags":["validation","cel-filter","list-users","invalid-argument"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}