{"record":{"id":"bdaabd9f20f55204","repo":"usememos/memos","slug":"s3-object-payload-is-missing","errorCode":null,"errorMessage":"S3 object payload is missing","messagePattern":"S3 object payload is missing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"store/attachment.go","lineNumber":288,"sourceCode":"\t\t\t}\n\t\t\treturn instanceStorageSetting, nil\n\t\t}\n\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.","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/store/attachment.go#L270-L306","documentation":"ResolveAttachmentS3Driver throws \"S3 object payload is missing\" when attachment.Payload is present but its oneof content does not carry an S3Object (e.g. it only holds a LOCAL file descriptor). Only S3-backed attachments can be resolved to an S3 storage driver.","triggerScenarios":"Calling S3 resolution on an attachment whose StorageType is LOCAL, or whose payload oneof is unset/holds a different message type.","commonSituations":"Instance switched from S3 to local storage but old code paths still try S3 resolution for all attachments; branching on the wrong field before choosing the resolution path.","solutions":["Branch on attachment.StorageType (or payload.GetS3Object() != nil) before calling ResolveAttachmentS3Driver","Use the local file path for LOCAL attachments instead of S3 resolution","Verify the payload oneof is set to s3_object when writing S3 attachments"],"exampleFix":"// before\ndriver, obj, err := store.ResolveAttachmentS3Driver(ctx, att) // att is LOCAL\n// after\nif att.Payload.GetS3Object() == nil {\n    return serveLocalFile(att)\n}\ndriver, obj, err := store.ResolveAttachmentS3Driver(ctx, att)","handlingStrategy":"type-guard","validationCode":"if attachment.Payload.GetS3Object() == nil {\n    // not S3-backed; use the local path instead\n}","typeGuard":"func isS3Attachment(a *store.Attachment) bool {\n    return a != nil && a.Payload != nil && a.Payload.GetS3Object() != nil\n}","tryCatchPattern":null,"preventionTips":["Branch on StorageType / payload oneof before choosing S3 vs local handling","Set the payload oneof correctly when creating attachments"],"tags":["store","attachment","s3","oneof"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}