hashicorp/terraform · error
failed to lock azure state
Error message
failed to lock azure state: %s
What it means
Thrown by StateMgr when a workspace state object does not yet exist and the backend tries to acquire a lease-based lock to initialize it, but client.Lock returns an error. Lock fails when another client already holds the lease, when a stale lease from a crashed run remains, or when the lock-info metadata write fails.
Solutions
- Run `terraform force-unlock <lock-id>` using the ID printed in the LockError info.
- If the other run is legitimate, wait for it to finish then retry.
- Inspect the lease via Azure portal or `az storage blob lease list` / `show`.
- If the lock info metadata is corrupt, break the lease manually via `az storage blob lease break`.
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: check whether the state blob is already leased before attempting to lock.
func isStateBlobLeased(ctx context.Context, acct, container, blob string) (bool, error) {
cmd := exec.CommandContext(ctx, "az", "storage", "blob", "show",
"--account-name", acct, "-c", container, "-n", blob, "--query", "properties.lease.status", "-o", "tsv")
out, err := cmd.Output()
if err != nil { return false, err }
return strings.TrimSpace(string(out)) == "locked", nil
} Try / catch
// On a lock failure, parse the LockError and offer the lock id for force-unlock.
stateMgr, diags := backend.StateMgr(name)
if diags.HasErrors() {
var le *statemgr.LockError
if errors.As(diags.Err(), &le) && le.Info != nil {
log.Printf("state is locked by %s; run: terraform force-unlock %s", le.Info.Operation, le.Info.ID)
}
} Prevention
- Coordinate team / CI access to a shared workspace to avoid overlapping runs.
- Always run `terraform force-unlock <id>` only after confirming the holder is dead.
- Monitor stale leases via a periodic `az storage blob lease list` script.
When it happens
Trigger: (a) Another terraform process already acquired the lease on the same state blob. (b) A prior run crashed without releasing (stale lease). (c) writeLockInfo failed (SetMetaData network/RBAC error). (d) AcquireLease itself failed (throttling).
Common situations: Concurrent `terraform apply` in two terminals against the same workspace; CI overlap; crashed run left a lease; long-held lock from interrupted operation.
Related errors
- state blob is already locked
- blob metadata was empty
- Error unlocking Azure state. Lock ID
- failed to retrieve lock info
- can't delete default state
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/e05ee4e97ff44011.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/azure/backend_state.go:119
accountName: b.accountName,
snapshot: b.snapshot,
}
stateMgr := &remote.State{Client: client}
// 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 {View on GitHub (pinned to d32a084675)