hashicorp/terraform · error
Error unlocking Azure state. Lock ID
Error message
Error unlocking Azure state. Lock ID: %s Error: %s You may have to force-unlock this state in order to use it again.
What it means
Produced by the lockUnlock helper in StateMgr when stateMgr.Unlock(lockId) fails during cleanup of the initialization flow. Indicates the lease could not be released after a partial write/refresh failure. The message instructs the user that a manual force-unlock may be required because the lease may still be held.
Solutions
- Run `terraform force-unlock <lock-id>` with the ID printed in the message.
- Verify the lease is gone via Azure portal / `az storage blob lease show`.
- If the blob was deleted, the unlock is moot — recreate the workspace state.
- Retry the original terraform command once the lock is cleared.
Defensive patterns
Strategy: try-catch
Try / catch
// After a failed init that may have left a lease, attempt an explicit force-unlock.
if err := stateMgr.Unlock(lockId); err != nil {
log.Printf("%s; capture lock id %s for manual force-unlock", err, lockId)
// surface lockId to the operator / CI log
} Prevention
- Always print the lock ID in CI logs so an operator can force-unlock quickly.
- Use `terraform force-unlock` as a documented recovery step in runbooks.
- Investigate transient network issues that interrupt the ReleaseLease call.
When it happens
Trigger: (a) Network/transport failure during the ReleaseLease call. (b) Lease ID was already released or expired. (c) Permissions revoked mid-run. (d) State blob was deleted out-of-band.
Common situations: Flaky network mid-init; very long init that exceeded lease validity (rare since this backend uses infinite-duration leases); operator manually broke the lease during a run.
Related errors
- blob metadata was empty
- failed to retrieve lock info
- state blob is already locked
- failed to lock azure state
- error snapshotting Blob
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/d35639c495dec685.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/azure/backend_state.go:125
// Grab the value
if err := stateMgr.RefreshState(); err != nil {
return nil, diags.Append(err)
}
//if this isn't the default state name, we need to create the object so
//it's listed by States.
if v := stateMgr.State(); v == nil {
// take a lock on this state while we write it
lockInfo := statemgr.NewLockInfo()
lockInfo.Operation = "init"
lockId, err := client.Lock(lockInfo)
if err != nil {
return nil, diags.Append(fmt.Errorf("failed to lock azure state: %s", err))
}
// Local helper function so we can call it multiple places
lockUnlock := func(parent error) error {
if err := stateMgr.Unlock(lockId); err != nil {
return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)
}
return parent
}
// Grab the value
if err := stateMgr.RefreshState(); err != nil {
err = lockUnlock(err)
return nil, diags.Append(err)
}
//if this isn't the default state name, we need to create the object so
//it's listed by States.
if v := stateMgr.State(); v == nil {
// If we have no state, we have to create an empty state
if err := stateMgr.WriteState(states.NewState()); err != nil {
err = lockUnlock(err)
return nil, diags.Append(err)
}
if err := stateMgr.PersistState(nil); err != nil {View on GitHub (pinned to d32a084675)