{"record":{"id":"ba9bce327ee2e6ab","repo":"hashicorp/terraform","slug":"state-is-already-unlocked","errorCode":null,"errorMessage":"state is already unlocked","messagePattern":"state is already unlocked","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/backend/remote-state/kubernetes/client.go","lineNumber":312,"sourceCode":"\t\treturn \"\", err\n\t}\n\n\treturn info.ID, err\n}\n\nfunc (c *RemoteClient) Unlock(id string) error {\n\tleaseName, err := c.createLeaseName()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tlease, err := c.getLease(leaseName)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif lease.Spec.HolderIdentity == nil {\n\t\treturn fmt.Errorf(\"state is already unlocked\")\n\t}\n\n\tlockInfo, err := c.getLockInfo(lease)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tlockErr := &statemgr.LockError{Info: lockInfo}\n\tif *lease.Spec.HolderIdentity != id {\n\t\tlockErr.Err = fmt.Errorf(\"lock id %q does not match existing lock\", id)\n\t\treturn lockErr\n\t}\n\n\tlease.Spec.HolderIdentity = nil\n\tremoveLockInfo(lease)\n\n\t_, err = c.kubernetesLeaseClient.Update(context.Background(), lease, metav1.UpdateOptions{})\n\tif err != nil {","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/backend/remote-state/kubernetes/client.go#L294-L330","documentation":"The Kubernetes backend's Unlock() found a lease whose HolderIdentity is nil, meaning the state is not currently held by anyone. Rather than silently succeeding, it returns this error so the caller knows the unlock was a no-op against an already-free state.","triggerScenarios":"Calling Unlock(id) (client.go:300-336) when the lease exists but lease.Spec.HolderIdentity == nil, i.e. a previous unlock or external deletion already cleared the holder.","commonSituations":"A duplicate/late unlock after the state was already released; a manual edit of the lease removing HolderIdentity; an earlier failed init that partially unlocked; Terraform retrying an unlock after the lease was cleared by another process.","solutions":["Treat this as informational: the state is already unlocked, so no force-unlock is needed.","Check whether another Terraform run or a teammate already unlocked the state.","If the lease is in a bad state (e.g. stale lock-info annotation with nil holder), manually clear the annotation or delete the lease after confirming no active run holds it."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Before unlocking, check whether the lease still holds a lock\nlease, err := c.getLease(name)\nif err != nil { return err }\nif lease.Spec.HolderIdentity == nil {\n    // nothing to unlock; treat as success\n    return nil\n}\nreturn c.Unlock(id)","typeGuard":"func isLeaseHeld(lease *coordinationv1.Lease) bool {\n    return lease != nil && lease.Spec.HolderIdentity != nil\n}","tryCatchPattern":null,"preventionTips":["Check the live lease holder before issuing an unlock.","Avoid redundant/duplicate unlock calls after failures.","Treat 'already unlocked' as benign, not an error to retry."],"tags":["kubernetes","remote-state","locking","terraform-backend"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T20:17:04.800Z"}