hashicorp/terraform · error

Secret does does not have

Error message

Secret does does not have %q label

What it means

Returned by deleteSecret when the targeted Secret lacks the tfstateKey label (value 'true') that the backend uses to mark managed Secrets (client.go:379-383). This is a safety guard preventing deletion of Secrets the backend did not create. Note: the message contains a typo ('does does') which is a known cosmetic bug in the source.

Solutions

  1. Confirm the Secret is actually a Terraform state Secret before acting; if so, re-add the tfstateKey=true label via kubectl label.
  2. If the Secret is unrelated, do not delete it — investigate the naming collision.
  3. Check for admission webhooks/mutation controllers that strip labels and exclude tfstate Secrets.
  4. Report/ignore the 'does does' duplicate wording; it is cosmetic.
Defensive patterns

Strategy: validation

Validate before calling

// Before deleting, confirm the Secret is backend-managed:
// sec, _ := getSecret(name)
// if sec.GetLabels()[tfstateKey] != "true" { /* not managed; refuse to delete */ }

Try / catch

// if err := client.deleteSecret(name); err != nil {
//   if strings.Contains(err.Error(), "Secret does does not have") {
//     // label missing; re-label if it is genuinely a state Secret, else investigate
//   }
// }

Prevention

When it happens

Trigger: deleteSecret is called on a Secret that was created manually or by another tool, or whose tfstateKey label was removed/never set. The label check (!ok || v != "true") fails.

Common situations: Manual relabeling or label-stripping by a mutation webhook; a Secret name collision with an unrelated Secret; the backend's label was overwritten by a kustomize/overlay; migrating label schemes.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/d9903fa66b46c9df. Report an issue: GitHub.

Appendix: source

Thrown at internal/backend/remote-state/kubernetes/client.go:382

func (c *RemoteClient) getSecret(name string) (*unstructured.Unstructured, error) {
	return c.kubernetesSecretClient.Get(context.Background(), name, metav1.GetOptions{})
}

func (c *RemoteClient) getLease(name string) (*coordinationv1.Lease, error) {
	return c.kubernetesLeaseClient.Get(context.Background(), name, metav1.GetOptions{})
}

func (c *RemoteClient) deleteSecret(name string) error {
	secret, err := c.getSecret(name)
	if err != nil {
		return err
	}

	labels := secret.GetLabels()
	v, ok := labels[tfstateKey]
	if !ok || v != "true" {
		return fmt.Errorf("Secret does does not have %q label", tfstateKey)
	}

	delProp := metav1.DeletePropagationBackground
	delOps := metav1.DeleteOptions{PropagationPolicy: &delProp}
	return c.kubernetesSecretClient.Delete(context.Background(), name, delOps)
}

func (c *RemoteClient) deleteLease(name string) error {
	secret, err := c.getLease(name)
	if err != nil {
		return err
	}

	labels := secret.GetLabels()
	v, ok := labels[tfstateKey]
	if !ok || v != "true" {
		return fmt.Errorf("Lease does does not have %q label", tfstateKey)
	}

View on GitHub (pinned to d32a084675)