JuliusBrussee/caveman · error
objectstore: list versions
Error message
objectstore: list versions %q: %w
What it means
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.
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.
Example fix
// before
if err := store.DeleteAllVersions(ctx, key); err != nil {
return err
}
// after
if err := store.DeleteAllVersions(ctx, key); err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return retryWithBackoff(ctx, func() error { return store.DeleteAllVersions(ctx, key) })
}
return fmt.Errorf("purge %s: %w", key, err)
} Defensive patterns
Strategy: retry
Validate before calling
func canListVersions(ctx context.Context, c *minio.Client, bucket string) error {
for obj := range c.ListObjects(ctx, bucket, minio.ListObjectsOptions{WithVersions: true, MaxKeys: 1}) {
return obj.Err
}
return nil
} Try / catch
if err := store.DeleteAllVersions(ctx, key); err != nil {
var mr minio.ErrorResponse
if errors.As(err, &mr) && mr.Code == "AccessDenied" {
return fmt.Errorf("missing s3:ListBucketVersions: %w", err)
}
return retryWithBackoff(ctx, err)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- objectstore: delete version
- objectstore: list page
- objectstore: list
- objectstore: delete
- objectstore: read
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/36a5cd62ae523056.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/objectstore/objectstore.go:327
}
return nil
}
func (m *minioStore) DeleteAllVersions(ctx context.Context, key string) error {
return m.deleteVersions(ctx, key, true)
}
func (m *minioStore) DeletePrefixAllVersions(ctx context.Context, prefix string) error {
return m.deleteVersions(ctx, prefix, false)
}
func (m *minioStore) deleteVersions(ctx context.Context, keyOrPrefix string, exact bool) error {
found := false
for obj := range m.client.ListObjects(ctx, m.bucket, minio.ListObjectsOptions{
Prefix: keyOrPrefix, Recursive: true, WithVersions: true,
}) {
if obj.Err != nil {
return fmt.Errorf("objectstore: list versions %q: %w", keyOrPrefix, obj.Err)
}
if exact && obj.Key != keyOrPrefix {
continue
}
found = true
if err := m.client.RemoveObject(ctx, m.bucket, obj.Key, minio.RemoveObjectOptions{VersionID: obj.VersionID}); err != nil {
return fmt.Errorf("objectstore: delete version %q (%q): %w", obj.Key, obj.VersionID, err)
}
}
if exact && !found {
// Non-versioned/S3-compatible backends may omit version listings. Ordinary
// deletion remains idempotent and covers that deployment shape.
return m.Delete(ctx, keyOrPrefix)
}
return nil
}
func (m *minioStore) Exists(ctx context.Context, key string) (bool, error) {View on GitHub (pinned to 3ee70a1026)