{"record":{"id":"2f33641825df77f3","repo":"usememos/memos","slug":"s3-object-key-is-missing","errorCode":null,"errorMessage":"S3 object key is missing","messagePattern":"S3 object key is missing","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"store/attachment.go","lineNumber":291,"sourceCode":"\t}\n\treturn nil, nil\n}\n\n// ResolveAttachmentS3Driver resolves the storage driver referenced by an S3\n// attachment payload, validating the payload and supplying the instance setting.\nfunc (s *Store) ResolveAttachmentS3Driver(ctx context.Context, attachment *Attachment) (storage.Driver, *storepb.AttachmentPayload_S3Object, error) {\n\tif attachment == nil {\n\t\treturn nil, nil, errors.New(\"attachment is missing\")\n\t}\n\tif attachment.Payload == nil {\n\t\treturn nil, nil, errors.New(\"attachment payload is missing\")\n\t}\n\ts3Object := attachment.Payload.GetS3Object()\n\tif s3Object == nil {\n\t\treturn nil, nil, errors.New(\"S3 object payload is missing\")\n\t}\n\tif s3Object.Key == \"\" {\n\t\treturn nil, nil, errors.New(\"S3 object key is missing\")\n\t}\n\n\tinstanceStorageSetting, err := s.GetInstanceStorageSetting(ctx)\n\tif err != nil {\n\t\treturn nil, nil, errors.Wrap(err, \"failed to get instance storage setting\")\n\t}\n\tdriver, err := ResolveStorageDriver(ctx, instanceStorageSetting, s3Object.StorageId, s3Object.S3Config)\n\tif err != nil {\n\t\treturn nil, nil, errors.Wrap(err, \"failed to resolve storage driver\")\n\t}\n\treturn driver, s3Object, nil\n}\n\n// AttachmentNeedsInstanceStorageSetting reports whether cleanup should load\n// the configured storage registry for an S3 attachment.\nfunc AttachmentNeedsInstanceStorageSetting(attachment *Attachment) bool {\n\tif attachment == nil || attachment.StorageType != storepb.AttachmentStorageType_S3 {\n\t\treturn false","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/store/attachment.go#L273-L309","documentation":"ResolveAttachmentS3Driver throws \"S3 object key is missing\" when the S3Object payload exists but its Key field is an empty string. Without a key there is no object address in the bucket, so presigning or reading would be meaningless; the function fails fast rather than issuing requests for \"\".","triggerScenarios":"Attachment rows whose payload.s3_object was stored with an empty key — usually via custom writes, migrations, or truncated serialization.","commonSituations":"Hand-crafted backfill scripts that set bucket but not key; payload protojson that dropped the key due to a field-name typo (e.g. \"objectKey\" vs \"key\"); deleted S3 objects leaving empty-key placeholders.","solutions":["Repair the stored payload so s3_object.key holds the real object key","Validate the key is non-empty at attachment-create time before persisting","If the blob is unrecoverable, delete the attachment record or mark it LOCAL-stubbed"],"exampleFix":"// before (writing)\npayload.S3Object = &storepb.AttachmentPayload_S3Object{BucketId: b, StorageId: s}\n// after\npayload.S3Object = &storepb.AttachmentPayload_S3Object{Key: objectKey, BucketId: b, StorageId: s}","handlingStrategy":"validation","validationCode":"if s3 := attachment.Payload.GetS3Object(); s3 == nil || s3.Key == \"\" {\n    return errors.New(\"attachment has no S3 object key\")\n}","typeGuard":"func validS3Key(s3 *storepb.AttachmentPayload_S3Object) bool {\n    return s3 != nil && s3.Key != \"\"\n}","tryCatchPattern":null,"preventionTips":["Validate the object key when writing S3 attachments; never persist empty keys","Audit backfill scripts to ensure the key field is copied"],"tags":["store","attachment","s3","object-key"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}