hashicorp/terraform · warning

state is already unlocked

Error message

state is already unlocked

What it means

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.

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

Solutions

  1. Treat this as informational: the state is already unlocked, so no force-unlock is needed.
  2. Check whether another Terraform run or a teammate already unlocked the state.
  3. 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.
Defensive patterns

Strategy: type-guard

Validate before calling

// Before unlocking, check whether the lease still holds a lock
lease, err := c.getLease(name)
if err != nil { return err }
if lease.Spec.HolderIdentity == nil {
    // nothing to unlock; treat as success
    return nil
}
return c.Unlock(id)

Type guard

func isLeaseHeld(lease *coordinationv1.Lease) bool {
    return lease != nil && lease.Spec.HolderIdentity != nil
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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