usememos/memos · error

attachment is missing

Error message

attachment is missing

What it means

ResolveAttachmentS3Driver guards its inputs in order: a nil *Attachment triggers "attachment is missing" before any payload inspection. The function needs a loaded attachment (including its Payload field) to resolve the S3 storage driver for operations like presigned URL generation or blob reads.

Solutions

  1. Check the GetAttachment result for nil before resolving the S3 driver
  2. Return a not-found error to the caller at the lookup site instead of proceeding
  3. Add a unit test covering nil attachment input

Example fix

// before
att, _ := store.GetAttachment(ctx, find)
driver, s3obj, err := store.ResolveAttachmentS3Driver(ctx, att)
// after
att, err := store.GetAttachment(ctx, find)
if err != nil {
    return err
}
if att == nil {
    return status.Error(codes.NotFound, "attachment not found")
}
driver, s3obj, err := store.ResolveAttachmentS3Driver(ctx, att)
Defensive patterns

Strategy: type-guard

Validate before calling

if attachment == nil {
    return errors.New("cannot resolve S3 driver: attachment not loaded")
}

Type guard

func hasAttachment(a *store.Attachment) bool { return a != nil }

Prevention

When it happens

Trigger: Calling ResolveAttachmentS3Driver with a nil attachment pointer, typically after a GetAttachment/FindAttachment lookup that returned nil without checking.

Common situations: Handling an attachment ID from a URL that no longer exists and passing the nil result straight to S3 resolution; refactors that moved the nil check out of the caller.

Related errors


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

Appendix: source

Thrown at store/attachment.go:281

func (s *Store) getAttachmentStorageCleanupInstanceSetting(ctx context.Context, attachments []*Attachment) (*storepb.InstanceStorageSetting, error) {
	for _, attachment := range attachments {
		if AttachmentNeedsInstanceStorageSetting(attachment) {
			instanceStorageSetting, err := s.GetInstanceStorageSetting(ctx)
			if err != nil {
				return nil, errors.Wrap(err, "failed to get instance storage setting")
			}
			return instanceStorageSetting, nil
		}
	}
	return nil, nil
}

// ResolveAttachmentS3Driver resolves the storage driver referenced by an S3
// attachment payload, validating the payload and supplying the instance setting.
func (s *Store) ResolveAttachmentS3Driver(ctx context.Context, attachment *Attachment) (storage.Driver, *storepb.AttachmentPayload_S3Object, error) {
	if attachment == nil {
		return nil, nil, errors.New("attachment is missing")
	}
	if attachment.Payload == nil {
		return nil, nil, errors.New("attachment payload is missing")
	}
	s3Object := attachment.Payload.GetS3Object()
	if s3Object == nil {
		return nil, nil, errors.New("S3 object payload is missing")
	}
	if s3Object.Key == "" {
		return nil, nil, errors.New("S3 object key is missing")
	}

	instanceStorageSetting, err := s.GetInstanceStorageSetting(ctx)
	if err != nil {
		return nil, nil, errors.Wrap(err, "failed to get instance storage setting")
	}
	driver, err := ResolveStorageDriver(ctx, instanceStorageSetting, s3Object.StorageId, s3Object.S3Config)
	if err != nil {

View on GitHub (pinned to 14d757ce1f)