{"record":{"id":"a7ed13f1d7a44e97","repo":"usememos/memos","slug":"invalidargument-a7ed13","errorCode":"InvalidArgument","errorMessage":"filter cannot be empty","messagePattern":"filter cannot be empty","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"server/router/api/v1/memo_view_service.go","lineNumber":226,"sourceCode":"\t}\n\tif err := s.authorizeMemoViewAccess(ctx, user); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfound, err := s.Store.RemoveUserMemoView(ctx, user.ID, memoViewID)\n\tif err != nil {\n\t\treturn nil, status.Errorf(codes.Internal, \"failed to delete memo view: %v\", err)\n\t}\n\tif !found {\n\t\treturn nil, status.Errorf(codes.NotFound, \"memo view not found\")\n\t}\n\n\treturn &emptypb.Empty{}, nil\n}\n\nfunc (s *APIV1Service) validateFilter(ctx context.Context, filterStr string) error {\n\tif filterStr == \"\" {\n\t\treturn errors.New(\"filter cannot be empty\")\n\t}\n\n\tengine, err := filter.DefaultEngine()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif _, err := engine.CompileToStatement(ctx, filterStr, filter.RenderOptions{Dialect: s.filterDialect()}); err != nil {\n\t\treturn errors.Wrap(err, \"failed to compile filter\")\n\t}\n\treturn nil\n}\n\n// filterDialect maps the configured database driver onto the filter engine dialect.\nfunc (s *APIV1Service) filterDialect() filter.DialectName {\n\tswitch s.Profile.Driver {\n\tcase \"mysql\":\n\t\treturn filter.DialectMySQL","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/server/router/api/v1/memo_view_service.go#L208-L244","documentation":"validateFilter is used by memo-view APIs (e.g. CreateMemoView) to check a user-supplied CEL filter string before persisting it. An empty string is rejected immediately with InvalidArgument; non-empty strings are compiled with the store dialect to catch syntax errors early. The empty check exists because compiling '' would produce a confusing downstream error.","triggerScenarios":"CreateMemoView / UpdateMemoView with filter = '' ; a view-creation form where the filter builder returned an empty string; programmatically creating a 'catch-all' view with no conditions.","commonSituations":"Filter editor cleared by the user but the request still sent; conditional filter construction (cond ? expr : '') leaking empty strings; clients assuming empty filter means 'match everything'.","solutions":["Require a non-empty filter in the view form before enabling save.","For a catch-all view, use a trivially true CEL expression appropriate to the schema (e.g. 'true' if supported by the compiler) instead of ''.","Test the filter string compiles before persisting (the endpoint does this for you; fail early client-side for better UX)."],"exampleFix":"// before\nawait viewClient.createMemoView({ view: { filter: filterExpr } }); // filterExpr = ''\n\n// after\nif (!filterExpr.trim()) throw new Error('Filter is required');\nawait viewClient.createMemoView({ view: { filter: filterExpr } });","handlingStrategy":"validation","validationCode":"if (!filterStr.trim()) throw new Error('Filter cannot be empty');\n// optionally pre-compile client-side with a CEL library for faster feedback","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Disable the save/create button until the filter builder has produced a non-empty expression.","Never map 'no conditions selected' to '' — decide between omitting the view and an explicit match-all.","Surface the server's compile error text to users; it names the CEL syntax problem."],"tags":["validation","memo-views","cel-filter","invalid-argument"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}