{"record":{"id":"2f3909265376000d","repo":"JuliusBrussee/caveman","slug":"objectstore-delete-version-q-q-w","errorCode":null,"errorMessage":"objectstore: delete version %q (%q): %w","messagePattern":"objectstore: delete version %q \\(%q\\): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/objectstore/objectstore.go","lineNumber":334,"sourceCode":"\nfunc (m *minioStore) DeletePrefixAllVersions(ctx context.Context, prefix string) error {\n\treturn m.deleteVersions(ctx, prefix, false)\n}\n\nfunc (m *minioStore) deleteVersions(ctx context.Context, keyOrPrefix string, exact bool) error {\n\tfound := false\n\tfor obj := range m.client.ListObjects(ctx, m.bucket, minio.ListObjectsOptions{\n\t\tPrefix: keyOrPrefix, Recursive: true, WithVersions: true,\n\t}) {\n\t\tif obj.Err != nil {\n\t\t\treturn fmt.Errorf(\"objectstore: list versions %q: %w\", keyOrPrefix, obj.Err)\n\t\t}\n\t\tif exact && obj.Key != keyOrPrefix {\n\t\t\tcontinue\n\t\t}\n\t\tfound = true\n\t\tif err := m.client.RemoveObject(ctx, m.bucket, obj.Key, minio.RemoveObjectOptions{VersionID: obj.VersionID}); err != nil {\n\t\t\treturn fmt.Errorf(\"objectstore: delete version %q (%q): %w\", obj.Key, obj.VersionID, err)\n\t\t}\n\t}\n\tif exact && !found {\n\t\t// Non-versioned/S3-compatible backends may omit version listings. Ordinary\n\t\t// deletion remains idempotent and covers that deployment shape.\n\t\treturn m.Delete(ctx, keyOrPrefix)\n\t}\n\treturn nil\n}\n\nfunc (m *minioStore) Exists(ctx context.Context, key string) (bool, error) {\n\t_, err := m.client.StatObject(ctx, m.bucket, key, minio.StatObjectOptions{})\n\tif err != nil {\n\t\tif minio.ToErrorResponse(err).Code == \"NoSuchKey\" {\n\t\t\treturn false, nil\n\t\t}\n\t\treturn false, fmt.Errorf(\"objectstore: stat %q: %w\", key, err)\n\t}","sourceCodeStart":316,"sourceCodeEnd":352,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/objectstore/objectstore.go#L316-L352","documentation":"deleteVersions, after finding a version to remove, calls RemoveObject with that VersionID; this error wraps a failure of that per-version removal and includes both the key and version ID. The operation aborts the remaining version deletions. The unusual two-%q format (\"delete version %q (%q)\") reflects key + versionID.","triggerScenarios":"DeleteAllVersions/DeletePrefixAllVersions iterating versions where RemoveObject with VersionID fails: permission denied (s3:DeleteObjectVersion), object-lock/retention hold on that version, or transient backend error mid-loop.","commonSituations":"Buckets with Object Lock / legal hold / retention period blocking version deletion; IAM missing DeleteObjectVersion; S3-compatible backends rejecting versioned deletes; a legally held version in an otherwise deletable prefix.","solutions":["Check the wrapped minio.ErrorResponse: AccessDenied means IAM needs s3:DeleteObjectVersion.","Look for Object Lock retention/legal holds on the reported version and lift them or wait out retention.","Make the loop resilient: collect failures per version and continue instead of aborting on first error.","Retry transient 5xx/SlowDown errors with backoff.","Verify backend support for versioned RemoveObject if using non-AWS S3-compatible storage."],"exampleFix":"// before\nfor obj := range versions {\n    if err := remove(ctx, obj); err != nil {\n        return err // aborts on first held version\n    }\n}\n// after\nvar errs []error\nfor obj := range versions {\n    if err := remove(ctx, obj); err != nil {\n        errs = append(errs, fmt.Errorf(\"%s@%s: %w\", obj.Key, obj.VersionID, err))\n    }\n}\nreturn errors.Join(errs...)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := store.DeleteAllVersions(ctx, key); err != nil {\n    var mr minio.ErrorResponse\n    if errors.As(err, &mr) {\n        switch mr.Code {\n        case \"AccessDenied\":\n            return fmt.Errorf(\"need s3:DeleteObjectVersion: %w\", err)\n        case \"InvalidObjectState\", \"ObjectNotUnderActiveHold\":\n            return fmt.Errorf(\"object lock blocks version deletion: %w\", err)\n        }\n    }\n    return err\n}","preventionTips":["Check Object Lock / legal hold status before bulk version purges.","Provision s3:DeleteObjectVersion permissions.","Collect per-version failures instead of aborting the whole purge.","Test against versioned buckets in staging, not only flat buckets."],"tags":["object-storage","delete","versioning","minio","permissions"],"backgroundTag":"database-write-failed","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}