hashicorp/terraform · error
Secret does does not have %q label
Error message
Secret does does not have %q label
What it means
deleteSecret() refuses to delete a Secret that lacks the backend's ownership label `tfstate=true`. It first GETs the secret, checks labels[tfstateKey]; if absent or not 'true' it aborts with this error (note the duplicated 'does does' wording in the source). This guard prevents deleting arbitrary user Secrets whose name happens to match the computed secret name.
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 c9def3e214)
Solutions
- Confirm the Secret is not a real state object; if it is legitimate but untagged, add the `tfstate=true` label before retrying deletion.
- If it is an unrelated Secret, rename it or rename your secret_suffix/namespace so names do not collide.
- Manually delete the offending Secret with kubectl if it is safe to do so, then re-run.
Example fix
# before - a colliding non-state Secret blocks workspace deletion terraform workspace delete myapp # error: Secret does not have tfstate label # after - tag the legitimate state Secret (or remove the colliding one) kubectl label secret tfstate-default-myapp tfstate=true -n default terraform workspace delete myapp
Defensive patterns
Strategy: validation
Validate before calling
// Verify the secret carries the ownership label before attempting deletion
secret, err := c.kubernetesSecretClient.Get(ctx, name, metav1.GetOptions{})
if err != nil { return err }
if v, ok := secret.GetLabels()["tfstate"]; !ok || v != "true" {
// not a backend-owned secret; do not delete via the backend
return nil
}
return c.kubernetesSecretClient.Delete(ctx, name, metav1.DeleteOptions{}) Type guard
func isBackendOwnedSecret(obj *unstructured.Unstructured) bool {
v, ok := obj.GetLabels()["tfstate"]
return ok && v == "true"
} Prevention
- Do not create Secrets whose names match the tfstate-<ws>-<suffix> pattern.
- If migrating from an unlabeled backend, tag existing state Secrets with tfstate=true.
- Use a unique secret_suffix/namespace to avoid name collisions.
When it happens
Trigger: Delete()/DeleteWorkspace flow calling deleteSecret(name) at client.go:373-388 against a Secret that exists but has no `tfstate=true` label, e.g. a manually created Secret or one created by an older backend version without the label.
Common situations: A pre-existing Secret with a colliding name (e.g. someone created 'tfstate-default-myapp' manually); state created by an older Terraform version that did not tag secrets with the tfstate label; manual label removal.
Related errors
- Lease does does not have %q label
- Failed to configure: %s
- secret_suffix must not end with '-<number>', got %q
- Failed to initialize kubernetes configuration: %s
- can't delete default state
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/d9903fa66b46c9df.
Report an issue: GitHub.