JuliusBrussee/caveman · error
objectstore: delete version
Error message
objectstore: delete version %q (%q): %w
What it means
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.
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.
Example fix
// before
for obj := range versions {
if err := remove(ctx, obj); err != nil {
return err // aborts on first held version
}
}
// after
var errs []error
for obj := range versions {
if err := remove(ctx, obj); err != nil {
errs = append(errs, fmt.Errorf("%s@%s: %w", obj.Key, obj.VersionID, err))
}
}
return errors.Join(errs...) Defensive patterns
Strategy: try-catch
Try / catch
if err := store.DeleteAllVersions(ctx, key); err != nil {
var mr minio.ErrorResponse
if errors.As(err, &mr) {
switch mr.Code {
case "AccessDenied":
return fmt.Errorf("need s3:DeleteObjectVersion: %w", err)
case "InvalidObjectState", "ObjectNotUnderActiveHold":
return fmt.Errorf("object lock blocks version deletion: %w", err)
}
}
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- objectstore: delete
- objectstore: list versions
- objectstore: stat
- objectstore: list page
- objectstore: list
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/2f3909265376000d.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/objectstore/objectstore.go:334
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) {
_, err := m.client.StatObject(ctx, m.bucket, key, minio.StatObjectOptions{})
if err != nil {
if minio.ToErrorResponse(err).Code == "NoSuchKey" {
return false, nil
}
return false, fmt.Errorf("objectstore: stat %q: %w", key, err)
}View on GitHub (pinned to 3ee70a1026)