{"record":{"id":"d9903fa66b46c9df","repo":"hashicorp/terraform","slug":"secret-does-does-not-have-q-label","errorCode":null,"errorMessage":"Secret does does not have %q label","messagePattern":"Secret does does not have %q label","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/kubernetes/client.go","lineNumber":382,"sourceCode":"\nfunc (c *RemoteClient) getSecret(name string) (*unstructured.Unstructured, error) {\n\treturn c.kubernetesSecretClient.Get(context.Background(), name, metav1.GetOptions{})\n}\n\nfunc (c *RemoteClient) getLease(name string) (*coordinationv1.Lease, error) {\n\treturn c.kubernetesLeaseClient.Get(context.Background(), name, metav1.GetOptions{})\n}\n\nfunc (c *RemoteClient) deleteSecret(name string) error {\n\tsecret, err := c.getSecret(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tlabels := secret.GetLabels()\n\tv, ok := labels[tfstateKey]\n\tif !ok || v != \"true\" {\n\t\treturn fmt.Errorf(\"Secret does does not have %q label\", tfstateKey)\n\t}\n\n\tdelProp := metav1.DeletePropagationBackground\n\tdelOps := metav1.DeleteOptions{PropagationPolicy: &delProp}\n\treturn c.kubernetesSecretClient.Delete(context.Background(), name, delOps)\n}\n\nfunc (c *RemoteClient) deleteLease(name string) error {\n\tsecret, err := c.getLease(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tlabels := secret.GetLabels()\n\tv, ok := labels[tfstateKey]\n\tif !ok || v != \"true\" {\n\t\treturn fmt.Errorf(\"Lease does does not have %q label\", tfstateKey)\n\t}","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/backend/remote-state/kubernetes/client.go#L364-L400","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before - a colliding non-state Secret blocks workspace deletion\nterraform workspace delete myapp   # error: Secret does not have tfstate label\n\n# after - tag the legitimate state Secret (or remove the colliding one)\nkubectl label secret tfstate-default-myapp tfstate=true -n default\nterraform workspace delete myapp","handlingStrategy":"validation","validationCode":"// Verify the secret carries the ownership label before attempting deletion\nsecret, err := c.kubernetesSecretClient.Get(ctx, name, metav1.GetOptions{})\nif err != nil { return err }\nif v, ok := secret.GetLabels()[\"tfstate\"]; !ok || v != \"true\" {\n    // not a backend-owned secret; do not delete via the backend\n    return nil\n}\nreturn c.kubernetesSecretClient.Delete(ctx, name, metav1.DeleteOptions{})","typeGuard":"func isBackendOwnedSecret(obj *unstructured.Unstructured) bool {\n    v, ok := obj.GetLabels()[\"tfstate\"]\n    return ok && v == \"true\"\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["kubernetes","remote-state","labels","secrets","terraform-backend"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}