thanos-io/thanos · error
delete
Error message
delete %s
What it means
Delete (block directory removal) failed deleting an object from the bucket during recursive block deletion. Deletion order matters (meta.json first, deletion-mark.json last) to avoid malformed partial blocks, so failures abort the deletion.
Solutions
- Check bucket delete permissions
- Retry after transient storage errors
- Verify no retention policy blocks object deletion
Example fix
// before
err := block.Delete(ctx, logger, bkt, id)
// after
if err := block.Delete(ctx, logger, bkt, id); err != nil {
if isAccessDenied(err) { return fmt.Errorf("grant s3:DeleteObject on bucket: %w", err) }
return err
} Defensive patterns
Strategy: retry
Try / catch
if err := block.Delete(ctx, logger, bkt, id); err != nil {
var denied interface{ AccessDenied() bool }
if strings.Contains(err.Error(), "delete ") && isAccessDenied(err) {
return fmt.Errorf("bucket credentials lack delete permission: %w", err)
}
return err
} Prevention
- Grant s3:DeleteObject (or GCS storage.objects.delete) to the compactor identity
- Disable/adjust Object Lock or retention rules on block buckets
- Audit IAM policies when switching to shared/read-restricted buckets
When it happens
Trigger: bkt.Delete on <ULID>/meta.json fails due to insufficient delete permissions, object-store errors, versioning/retention policies, or network failure.
Common situations: IAM policy allows read but not delete (common on shared buckets); S3 Object Lock / retention or soft-delete policies; bucket configured read-only for compactor; transient backend errors.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/9d43cc2a2bbfe87c.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/block/block.go:215
// Delete removes directory that is meant to be block directory.
// NOTE: Always prefer this method for deleting blocks.
// - We have to delete block's files in the certain order (meta.json first and deletion-mark.json last)
// to ensure we don't end up with malformed partial blocks. Thanos system handles well partial blocks
// only if they don't have meta.json. If meta.json is present Thanos assumes valid block.
// - This avoids deleting empty dir (whole bucket) by mistake.
func Delete(ctx context.Context, logger log.Logger, bkt objstore.Bucket, id ulid.ULID) error {
metaFile := path.Join(id.String(), MetaFilename)
deletionMarkFile := path.Join(id.String(), metadata.DeletionMarkFilename)
// Delete block meta file.
ok, err := bkt.Exists(ctx, metaFile)
if err != nil {
return errors.Wrapf(err, "stat %s", metaFile)
}
if ok {
if err := bkt.Delete(ctx, metaFile); err != nil {
return errors.Wrapf(err, "delete %s", metaFile)
}
level.Debug(logger).Log("msg", "deleted file", "file", metaFile, "bucket", bkt.Name())
}
// Delete the block objects, but skip:
// - The metaFile as we just deleted. This is required for eventual object storages (list after write).
// - The deletionMarkFile as we'll delete it at last.
err = deleteDirRec(ctx, logger, bkt, id.String(), func(name string) bool {
return name == metaFile || name == deletionMarkFile
})
if err != nil {
return err
}
// Delete block deletion mark.
ok, err = bkt.Exists(ctx, deletionMarkFile)
if err != nil {
return errors.Wrapf(err, "stat %s", deletionMarkFile)View on GitHub (pinned to 35b8b99117)