{"record":{"id":"36a5cd62ae523056","repo":"JuliusBrussee/caveman","slug":"objectstore-list-versions-q-w","errorCode":null,"errorMessage":"objectstore: list versions %q: %w","messagePattern":"objectstore: list versions %q: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/objectstore/objectstore.go","lineNumber":327,"sourceCode":"\t}\n\treturn nil\n}\n\nfunc (m *minioStore) DeleteAllVersions(ctx context.Context, key string) error {\n\treturn m.deleteVersions(ctx, key, true)\n}\n\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) {","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/objectstore/objectstore.go#L309-L345","documentation":"deleteVersions enumerates all versions of a key/prefix with ListObjects(WithVersions:true); this error wraps a per-entry obj.Err from that listing stream. The delete operation aborts before removing anything further because the version inventory itself could not be read. The key or prefix is embedded and the underlying error preserved with %w.","triggerScenarios":"Calling DeleteAllVersions or DeletePrefixAllVersions when ListObjects with WithVersions:true yields an entry with obj.Err != nil: listing permission missing (s3:ListBucketVersions), interrupted stream, or backend error mid-pagination.","commonSituations":"Bucket with versioning enabled but IAM lacking ListBucketVersions; non-AWS S3-compatible backends (e.g. some MinIO versions, Ceph) returning errors on versioned listings; expired credentials mid-iteration; network drop during a long prefix listing.","solutions":["Grant s3:ListBucketVersions (and s3:GetObjectVersion) on the bucket for versioned listings.","If the backend does not support version listing, note deleteVersions falls back to ordinary Delete only for exact keys — for prefixes you must delete directly.","Retry the operation; version listing is paginated and transient failures are common on large prefixes.","Check backend compatibility for WithVersions on S3-compatible stores.","Inspect the wrapped error for throttling (SlowDown) and back off."],"exampleFix":"// before\nif err := store.DeleteAllVersions(ctx, key); err != nil {\n    return err\n}\n// after\nif err := store.DeleteAllVersions(ctx, key); err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        return retryWithBackoff(ctx, func() error { return store.DeleteAllVersions(ctx, key) })\n    }\n    return fmt.Errorf(\"purge %s: %w\", key, err)\n}","handlingStrategy":"retry","validationCode":"func canListVersions(ctx context.Context, c *minio.Client, bucket string) error {\n    for obj := range c.ListObjects(ctx, bucket, minio.ListObjectsOptions{WithVersions: true, MaxKeys: 1}) {\n        return obj.Err\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := store.DeleteAllVersions(ctx, key); err != nil {\n    var mr minio.ErrorResponse\n    if errors.As(err, &mr) && mr.Code == \"AccessDenied\" {\n        return fmt.Errorf(\"missing s3:ListBucketVersions: %w\", err)\n    }\n    return retryWithBackoff(ctx, err)\n}","preventionTips":["Grant s3:ListBucketVersions wherever versioned deletion is used.","Bound prefix size to keep version listings short.","Add backoff on SlowDown during large purges.","Verify backend supports WithVersions before relying on the fallback path."],"tags":["object-storage","list","versioning","minio"],"backgroundTag":"database-query-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"}