hashicorp/terraform · warning
state is already unlocked
Error message
state is already unlocked
What it means
Returned by the k8s backend Unlock when the Lease's HolderIdentity is nil (client.go:311-313). A nil holder means no one currently holds the lock, so there is nothing to unlock. This is a plain error (not a LockError) because there is no lock info to attach.
Solutions
- Treat as benign if state is expected to be unlocked — verify with kubectl get lease <name> -o yaml.
- Ensure Unlock is called exactly once per acquired lock; guard against double-unlock in your code.
- If a real lock should exist, investigate what cleared HolderIdentity (manual edit, concurrent run).
- Avoid force-unlock unless the Lease truly shows a holder you cannot release.
Defensive patterns
Strategy: try-catch
Validate before calling
// Check the Lease holder before attempting unlock:
// lease, _ := getLease(name)
// if lease.Spec.HolderIdentity == nil { /* already unlocked, skip */ } Try / catch
// if err := client.Unlock(id); err != nil {
// if strings.Contains(err.Error(), "state is already unlocked") { /* benign */ }
// } Prevention
- Call Unlock at most once per Lock; track lock state in your caller.
- Before unlocking, kubectl get lease <name> -o yaml to confirm a holder exists.
- Avoid manual edits of the Lease HolderIdentity.
When it happens
Trigger: Calling Unlock(id) when the state was never locked, or was already unlocked by a prior call, or the Lease was manually cleared. Also after a force-unlock that nilled HolderIdentity.
Common situations: Terraform retrying an unlock after a partial failure that already cleared the holder; manual kubectl editing of the Lease; a prior run that unlocked but did not finish cleanly; calling unlock with a stale lock ID after someone else unlocked.
Related errors
- lock id does not match existing lock
- failed to delete the lock file
- failed to read the body of the S3 object
- failed to retrieve lock info for lock ID
- failed to unlock both S3 and DynamoDB: S3 error
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/ba9bce327ee2e6ab.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/kubernetes/client.go:312
return "", err
}
return info.ID, err
}
func (c *RemoteClient) Unlock(id string) error {
leaseName, err := c.createLeaseName()
if err != nil {
return err
}
lease, err := c.getLease(leaseName)
if err != nil {
return err
}
if lease.Spec.HolderIdentity == nil {
return fmt.Errorf("state is already unlocked")
}
lockInfo, err := c.getLockInfo(lease)
if err != nil {
return err
}
lockErr := &statemgr.LockError{Info: lockInfo}
if *lease.Spec.HolderIdentity != id {
lockErr.Err = fmt.Errorf("lock id %q does not match existing lock", id)
return lockErr
}
lease.Spec.HolderIdentity = nil
removeLockInfo(lease)
_, err = c.kubernetesLeaseClient.Update(context.Background(), lease, metav1.UpdateOptions{})
if err != nil {View on GitHub (pinned to d32a084675)