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

  1. Omit the filter field (or send a valid non-empty filter) instead of an empty string
  2. Fix client concatenation: only include the filter when the components list is non-empty
  3. 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

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


AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15). Data as JSON: /api/errors/6b4f915d8922a9c1. Report an issue: GitHub.