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

  1. Inspect the wrapped API error for the Tencent error code.
  2. Grant `tag:DeleteTag` (and `tag:DescribeTags`) to the principal.
  3. If the tag is already gone, treat the unlock as complete (the retry loop in cosUnlock should converge, but if not, force-unlock).
  4. 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

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


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)