hashicorp/terraform · error
failed to empty bucket %v: %v
Error message
failed to empty bucket %v: %v
What it means
Raised by deleteBucket(recursive=true) when getBucket("") fails while listing objects prior to bucket deletion. The recursive path enumerates all objects so it can delete them before removing the bucket; if listing fails (and the error is not 'not exists'), the emptying step aborts. The message is 'failed to empty bucket'.
Source
Thrown at internal/backend/remote-state/cos/client.go:333
}
if err != nil {
return fmt.Errorf("failed to create bucket %v: %v", c.bucket, err)
}
return nil
}
// deleteBucket delete cos bucket
func (c *remoteClient) deleteBucket(recursive bool) error {
if recursive {
obs, err := c.getBucket("")
if err != nil {
if strings.Contains(err.Error(), "not exists") {
return nil
}
log.Printf("[DEBUG] deleteBucket %s: empty bucket error: %v", c.bucket, err)
return fmt.Errorf("failed to empty bucket %v: %v", c.bucket, err)
}
for _, v := range obs {
c.deleteObject(v.Key)
}
}
rsp, err := c.cosClient.Bucket.Delete(c.cosContext)
if rsp == nil {
log.Printf("[DEBUG] deleteBucket %s: error: %v", c.bucket, err)
return fmt.Errorf("failed to delete bucket %v: %v", c.bucket, err)
}
defer rsp.Body.Close()
log.Printf("[DEBUG] deleteBucket %s: code: %d, error: %v", c.bucket, rsp.StatusCode, err)
if rsp.StatusCode == 404 {
return nil
}
View on GitHub (pinned to c9def3e214)
Solutions
- Grant the principal 'cos:GetBucket' (list) in addition to delete permissions.
- Retry the destroy; transient 5xx/list failures often clear.
- If the bucket is huge, manually empty it with coscli/lifecycle rules first, then re-run with the bucket already empty.
- Confirm connectivity and credentials are still valid at destroy time (long sessions may see token expiry).
Example fix
# manually empty then re-run destroy coscli rm -r cos://tf-state-1234567890/ terraform destroy # CAM: ensure cos:GetBucket + cos:DeleteObject + cos:DeleteBucket are granted
Defensive patterns
Strategy: validation
Validate before calling
// Before recursive delete, confirm list permission.
func canListBucket(ctx context.Context, c *cos.Client) error {
_, _, err := c.Bucket.Get(ctx, &cos.BucketGetOptions{Prefix: ""})
return err
} Try / catch
if err := c.deleteBucket(true); err != nil {
if strings.Contains(err.Error(), "failed to empty bucket") {
// grant cos:GetBucket, or empty manually then retry
}
} Prevention
- Grant cos:GetBucket + cos:DeleteObject + cos:DeleteBucket to the destroy principal.
- For huge buckets, empty out-of-band first.
- Watch for STS token expiry during long destroys.
When it happens
Trigger: At client.go:326-334: getBucket("") returns an error whose message does not contain 'not exists'. Occurs when listing is denied (no cos:GetBucket), the COS service errors (5xx), or a network error during the destroy-time enumeration.
Common situations: Running 'terraform destroy' that also removes the backend bucket; principal has DeleteBucket but not ListBucket; very large bucket where list times out; transient COS 5xx during teardown.
Related errors
- failed to delete bucket %v: %v
- bucket %s not exists
- failed to delete file %v: %v
- failed to create bucket %v: %v
- failed to create tag: %s -> %s: %s
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/bdc805d40f5bda14.
Report an issue: GitHub.