usememos/memos · error
InvalidArgument
InvalidArgument
Error message
filter cannot be empty
What it means
validateAttachmentFilter rejects an empty filter string before compiling. Any API that takes an attachment filter (e.g. attachment listing with a filter param) requires a non-empty value; passing "" or an unset-but-forwarded parameter fails with InvalidArgument.
Source
Thrown at server/router/api/v1/attachment_service.go:535
}
if err := s.Store.ApplyMemoMutation(ctx, &store.MemoMutation{
MemoID: memo.ID,
MemoCreatorID: memo.CreatorID,
ExpectedMemoContent: memo.Content,
RemovedAttachmentIDs: removedIDs,
}); err != nil {
if errors.Is(err, store.ErrMemoMutationConflict) {
return status.Errorf(codes.FailedPrecondition, "memo state changed: %v", err)
}
return status.Errorf(codes.Internal, "failed to detach attachments: %v", err)
}
}
return nil
}
func (s *APIV1Service) validateAttachmentFilter(ctx context.Context, filterStr string) error {
if filterStr == "" {
return errors.New("filter cannot be empty")
}
engine, err := filter.DefaultAttachmentEngine()
if err != nil {
return err
}
if _, err := engine.CompileToStatement(ctx, filterStr, filter.RenderOptions{Dialect: s.filterDialect()}); err != nil {
return errors.Wrap(err, "failed to compile filter")
}
return nil
}
// checkAttachmentAccess verifies the user has permission to access the attachment.
// For unlinked attachments (no memo), only the creator can access.
// For linked attachments, access follows the memo's visibility rules.
func (s *APIV1Service) checkAttachmentAccess(ctx context.Context, attachment *store.Attachment) error {
// For unlinked attachments, only the creator can access.View on GitHub (pinned to 14d757ce1f)
Solutions
- Omit the filter field (or send a valid non-empty filter) instead of an empty string
- Fix client concatenation: only include the filter when the components list is non-empty
- If the intent is 'match everything', use a trivially true filter like `id > 0` if the grammar supports it, or leave filter unset
Example fix
// before
req := &apiv1.ListAttachmentsRequest{Filter: ptr(filterString)} // "" when no tags
// after
if filterString != "" { req.Filter = ptr(filterString) } Defensive patterns
Strategy: validation
Validate before calling
// Build the request so the filter field is only set when non-empty
req := &apiv1.ListAttachmentsRequest{}
if filterStr != "" {
req.Filter = &wrapperspb.StringValue{Value: filterStr}
}
// Also pre-compile to catch grammar errors early:
if filterStr != "" {
if _, err := filter.DefaultAttachmentEngine().CompileToStatement(ctx, filterStr, opts); err != nil {
return fmt.Errorf("invalid filter: %w", err)
}
} Try / catch
// Map to InvalidArgument with guidance
if err := s.validateAttachmentFilter(ctx, req.GetFilter()); err != nil {
if strings.Contains(err.Error(), "cannot be empty") {
return status.Errorf(codes.InvalidArgument, "filter must be a non-empty expression; omit the field entirely for no filtering")
}
return status.Errorf(codes.InvalidArgument, "%v", err)
} Prevention
- Omit optional filter fields instead of sending empty strings
- Guard concatenation-built filters with an emptiness check
- Validate filters at authoring time with the same engine
When it happens
Trigger: Calling the attachment list/search RPC with filter = "" — commonly a client that always includes the filter field, building it as `"tag in [" + strings.Join(tags, ", ") + "]"` which becomes empty when no tags are selected.
Common situations: Frontends sending filter unconditionally even when the user applied no filter; string concatenation producing empty strings; API consumers defaulting the field to "" instead of omitting it.
Related errors
- filter expression is empty
- filter must evaluate to a boolean value
- invalid use of in operator
- text match expects exactly one argument
- tags membership requires string literal
AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15).
Data as JSON: /api/errors/6b4f915d8922a9c1.
Report an issue: GitHub.