hashicorp/terraform · error
failed to delete tag
Error message
failed to delete tag: %s -> %s: %s
What it means
Returned by DeleteTag() when the Tencent Cloud Tag service rejects the DeleteTag call. The COS backend removes the distributed lock tag during Unlock; if the tag API call fails, the unlock cannot complete and the lock may remain, blocking subsequent runs. The key, value, and underlying API error are surfaced.
Solutions
- Inspect the wrapped API error for the Tencent error code.
- Grant `tag:DeleteTag` (and `tag:DescribeTags`) to the principal.
- If the tag is already gone, treat the unlock as complete (the retry loop in cosUnlock should converge, but if not, force-unlock).
- For transient failures, retry `terraform force-unlock <ID>` after the Tag service recovers.
Example fix
// before: principal has cos:* but not tag:DeleteTag, unlock fails leaving state locked
// after: add Tag delete permission
{
"statement":[{"effect":"allow","action":["tag:CreateTag","tag:DeleteTag","tag:DescribeTags"],"resource":"*"}]
} Defensive patterns
Strategy: validation
Validate before calling
// Before relying on unlock, confirm Tag delete permission
func canDeleteLockTag(ctx context.Context, tagClient *tag.Client, key, value string) error {
req := tag.NewDeleteTagRequest()
req.TagKey, req.TagValue = &key, &value
_, err := tagClient.DeleteTag(req)
if err != nil && (strings.Contains(err.Error(), "AuthFailure") || strings.Contains(err.Error(), "Unauthorized")) {
return fmt.Errorf("principal lacks tag:DeleteTag: %w", err)
}
return nil // not-found is acceptable for a probe
} Type guard
func isTagDeletePermissionError(err error) bool {
s := err.Error()
return strings.Contains(s, "AuthFailure") || strings.Contains(s, "UnauthorizedOperation")
} Try / catch
// cosUnlock already retries 30x; if it still fails, classify:
if isTagDeletePermissionError(err) {
// not retryable — surface clear guidance
return fmt.Errorf("grant tag:DeleteTag to allow unlock: %w", err)
} Prevention
- Grant the Tag service delete/describe permissions alongside COS permissions.
- Avoid racing two Terraform runs against the same state (both will fight over the lock tag).
- Do not manually remove lock tags unless you are certain no run is active.
- After any unlock failure, run `terraform force-unlock <ID>` once the permission/transient issue is resolved.
When it happens
Trigger: c.tagClient.DeleteTag(request) returns a non-nil error. Causes: missing/denied `tag:DeleteTag` permission, the tag was already deleted by a concurrent unlock (race), the tag service is degraded, or the tag key/value does not match what exists.
Common situations: Sub-account lacks Tag delete permission; two Terraform runs racing to unlock; the lock tag was manually removed mid-unlock; transient Tag API 5xx; cosUnlock retries up to 30 times then surfaces the last error.
Related errors
- failed to create tag
- bucket not exists
- failed to empty bucket
- lock file not exists
- failed to create bucket
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/8281b15772d42721.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/cos/client.go:446
_, err := c.tagClient.CreateTag(request)
log.Printf("[DEBUG] create tag %s:%s: error: %v", key, value, err)
if err != nil {
return fmt.Errorf("failed to create tag: %s -> %s: %s", key, value, err)
}
return nil
}
// DeleteTag create tag by key and value
func (c *remoteClient) DeleteTag(key, value string) error {
request := tag.NewDeleteTagRequest()
request.TagKey = &key
request.TagValue = &value
_, err := c.tagClient.DeleteTag(request)
log.Printf("[DEBUG] delete tag %s:%s: error: %v", key, value, err)
if err != nil {
return fmt.Errorf("failed to delete tag: %s -> %s: %s", key, value, err)
}
return nil
}
View on GitHub (pinned to d32a084675)