hashicorp/terraform · error

Lease does does not have

Error message

Lease does does not have %q label

What it means

Returned by deleteLease when the targeted Lease lacks the tfstateKey label (client.go:396-400). Mirrors deleteSecret's guard: only Leases the backend created (and labeled) may be deleted. Note the same 'does does' typo as the Secret variant.

Solutions

  1. Verify the Lease is a Terraform lock Lease; if so, re-apply the tfstateKey=true label with kubectl label lease.
  2. If unrelated, resolve the naming collision rather than deleting.
  3. Audit admission webhooks/controllers for label stripping on the coordination.k8s.io namespace.
  4. The 'does does' typo is cosmetic — safe to ignore.
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

// if err := client.deleteLease(name); err != nil {
//   if strings.Contains(err.Error(), "Lease does does not have") {
//     // re-label if genuinely a state Lease, else investigate collision
//   }
// }

Prevention

When it happens

Trigger: deleteLease is invoked on a Lease without the tfstateKey=true label — either manually created, label-stripped by a webhook, or a name collision with an unrelated coordination Lease.

Common situations: A mutation/label-stripping webhook removing labels; manual cleanup that removed labels; name collision with application Leases; cross-namespace label policy enforcement.

Related errors


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

Appendix: source

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

	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)
	}

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

func (c *RemoteClient) createSecretName(idx int) (string, error) {
	secretName := strings.Join([]string{tfstateKey, c.workspace, c.nameSuffix}, "-")

	if idx > 0 {
		secretName = fmt.Sprintf("%s-part-%d", secretName, idx)
	}

	errs := validation.IsDNS1123Subdomain(secretName)
	if len(errs) > 0 {
		k8sInfo := `
This is a requirement for Kubernetes secret names. 

View on GitHub (pinned to d32a084675)