hashicorp/terraform · error
Error unlocking Consul state. Lock ID: %s Error: %s You ma
Error message
Error unlocking Consul state. Lock ID: %s Error: %s You may have to force-unlock this state in order to use it again. The Consul backend acquires a lock during initialization to ensure the minimum required key/values are prepared.
What it means
During StateMgr init, the lockUnlock helper (backend_state.go:114) releases the lock it acquired; if Unlock fails (session destroyed, Consul error, lock already invalidated), it surfaces this multi-line error indicating a force-unlock may be required to use the state again.
Source
Thrown at internal/backend/remote-state/consul/backend_state.go:114
// the default state always exists
if name == backend.DefaultStateName {
return stateMgr, nil
}
// Grab a lock, we use this to write an empty state if one doesn't
// exist already. We have to write an empty state as a sentinel value
// so States() knows it exists.
lockInfo := statemgr.NewLockInfo()
lockInfo.Operation = "init"
lockId, err := stateMgr.Lock(lockInfo)
if err != nil {
return nil, diags.Append(fmt.Errorf("failed to lock state in Consul: %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 we have no state, we have to create an empty state
if v := stateMgr.State(); v == nil {
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 c9def3e214)
Solutions
- Run `terraform force-unlock <lock-id>` using the ID printed in the message.
- Verify Consul health and retry the original operation.
- If force-unlock fails, destroy the orphaned session via the Consul UI/API.
Defensive patterns
Strategy: fallback
Validate before calling
# before retrying, clear any orphaned lock for the workspace consul kv get "tfstate/<path>/.lockinfo" >/dev/null 2>&1 \ && terraform force-unlock "$(consul kv get tfstate/<path>/.lockinfo | jq -r .ID)" \ || echo "no stale lock to clear"
Try / catch
// on unlock failure, fall back to force-unlock by the reported id
if err := sm.Unlock(lockID); err != nil {
log.Printf("unlock failed (%v); attempting force-unlock of %s", err, lockID)
if fuErr := sm.Unlock(lockID); fuErr != nil {
return fmt.Errorf("force-unlock also failed for %s: %w", lockID, fuErr)
}
} Prevention
- Always let terraform exit cleanly so locks release normally.
- Verify Consul session TTLs exceed your longest plan/apply.
- Keep force-unlock as a documented recovery runbook step.
When it happens
Trigger: A lock was acquired but a subsequent RefreshState/WriteState/PersistState failed, and the cleanup Unlock also failed during the error-handling path.
Common situations: Consul session expired mid-init; transient Consul errors during teardown; the lock was invalidated elsewhere between acquire and release.
Related errors
- failed to lock state in Consul: %s
- consul lock was lost
- failed to lock azure state: %s
- Error unlocking Azure state. Lock ID: %s Error: %s You may
- state blob is already locked
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/819034e32b9fd216.
Report an issue: GitHub.