{"record":{"id":"2ad132a03ee6522f","repo":"usememos/memos","slug":"filter-expression-is-empty","errorCode":null,"errorMessage":"filter expression is empty","messagePattern":"filter expression is empty","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/filter/engine.go","lineNumber":50,"sourceCode":"\t\tnowFunc: time.Now,\n\t}, nil\n}\n\n// Program stores a compiled filter condition.\ntype Program struct {\n\tschema    Schema\n\tcondition Condition\n}\n\n// ConditionTree exposes the underlying condition tree.\nfunc (p *Program) ConditionTree() Condition {\n\treturn p.condition\n}\n\n// Compile parses the filter string into an executable program.\nfunc (e *Engine) Compile(_ context.Context, filter string) (*Program, error) {\n\tif strings.TrimSpace(filter) == \"\" {\n\t\treturn nil, errors.New(\"filter expression is empty\")\n\t}\n\n\tast, issues := e.env.Compile(filter)\n\tif issues != nil && issues.Err() != nil {\n\t\treturn nil, errors.Wrap(issues.Err(), \"failed to compile filter\")\n\t}\n\tparsed, err := cel.AstToParsedExpr(ast)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to convert AST\")\n\t}\n\n\tcond, err := buildCondition(parsed.GetExpr(), parseContext{schema: e.schema, now: e.nowFunc()})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Program{\n\t\tschema:    e.schema,","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/filter/engine.go#L32-L68","documentation":"Returned by Engine.Compile (internal/filter/engine.go) when the CEL filter string is empty or whitespace-only after TrimSpace. An empty filter has no condition tree to execute, so compilation is rejected up front rather than producing a nil program.","triggerScenarios":"Calling Engine.Compile with \"\", \"   \", or a value that trims to empty — e.g., a ListMemos request with filter unset-but-empty-string, a UI saved-search whose filter was cleared, or trimming user input before compile.","commonSituations":"Frontend sending filter: \"\" instead of omitting the field; saved views with cleared filters; scripts copying an example but leaving the filter blank.","solutions":["Omit the filter field entirely (proto optional/empty string means 'no filter') when you want all results.","Trim input upstream and treat empty as 'match everything' instead of compiling it.","Provide at least one boolean expression, e.g., \"pinned == true\"."],"exampleFix":"// before\nprog, err := engine.Compile(ctx, strings.TrimSpace(filter)) // \"\" -> error\n\n// after\nif strings.TrimSpace(filter) == \"\" {\n  return listAll(ctx) // no filter means match everything\n}\nprog, err := engine.Compile(ctx, filter)","handlingStrategy":"validation","validationCode":"// Go — treat empty as 'no filter' before compiling\ntrimmed := strings.TrimSpace(filter)\nif trimmed == \"\" {\n  return engine.ListAll(ctx) // or return an empty Program that matches everything\n}\nprog, err := engine.Compile(ctx, trimmed)","typeGuard":null,"tryCatchPattern":"prog, err := engine.Compile(ctx, filter)\nif err != nil {\n  if strings.Contains(err.Error(), \"filter expression is empty\") {\n    return status.Errorf(codes.InvalidArgument, \"filter must not be empty; omit the field to list all\")\n  }\n  return err\n}","preventionTips":["Trim user input and branch on empty before calling Compile.","In proto/API clients, omit optional filter fields instead of sending \"\".","Reject empty filters at save time for stored views."],"tags":["cel","filter","validation","go"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}