{"record":{"id":"819034e32b9fd216","repo":"hashicorp/terraform","slug":"error-unlocking-consul-state-lock-id-s-error","errorCode":null,"errorMessage":"Error unlocking Consul state. Lock ID: %s\n\nError: %s\n\nYou may have to force-unlock this state in order to use it again.\nThe Consul backend acquires a lock during initialization to ensure\nthe minimum required key/values are prepared.","messagePattern":"Error unlocking Consul state\\. Lock ID: (.+?)\n\nError: (.+?)\n\nYou may have to force-unlock this state in order to use it again\\.\nThe Consul backend acquires a lock during initialization to ensure\nthe minimum required key/values are prepared\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/consul/backend_state.go","lineNumber":114,"sourceCode":"\t// the default state always exists\n\tif name == backend.DefaultStateName {\n\t\treturn stateMgr, nil\n\t}\n\n\t// Grab a lock, we use this to write an empty state if one doesn't\n\t// exist already. We have to write an empty state as a sentinel value\n\t// so States() knows it exists.\n\tlockInfo := statemgr.NewLockInfo()\n\tlockInfo.Operation = \"init\"\n\tlockId, err := stateMgr.Lock(lockInfo)\n\tif err != nil {\n\t\treturn nil, diags.Append(fmt.Errorf(\"failed to lock state in Consul: %s\", err))\n\t}\n\n\t// Local helper function so we can call it multiple places\n\tlockUnlock := func(parent error) error {\n\t\tif err := stateMgr.Unlock(lockId); err != nil {\n\t\t\treturn fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)\n\t\t}\n\n\t\treturn parent\n\t}\n\n\t// Grab the value\n\tif err := stateMgr.RefreshState(); err != nil {\n\t\terr = lockUnlock(err)\n\t\treturn nil, diags.Append(err)\n\t}\n\n\t// If we have no state, we have to create an empty state\n\tif v := stateMgr.State(); v == nil {\n\t\tif err := stateMgr.WriteState(states.NewState()); err != nil {\n\t\t\terr = lockUnlock(err)\n\t\t\treturn nil, diags.Append(err)\n\t\t}\n\t\tif err := stateMgr.PersistState(nil); err != nil {","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/backend/remote-state/consul/backend_state.go#L96-L132","documentation":"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.","triggerScenarios":"A lock was acquired but a subsequent RefreshState/WriteState/PersistState failed, and the cleanup Unlock also failed during the error-handling path.","commonSituations":"Consul session expired mid-init; transient Consul errors during teardown; the lock was invalidated elsewhere between acquire and release.","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."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"# before retrying, clear any orphaned lock for the workspace\nconsul kv get \"tfstate/<path>/.lockinfo\" >/dev/null 2>&1 \\\n  && terraform force-unlock \"$(consul kv get tfstate/<path>/.lockinfo | jq -r .ID)\" \\\n  || echo \"no stale lock to clear\"","typeGuard":null,"tryCatchPattern":"// on unlock failure, fall back to force-unlock by the reported id\nif err := sm.Unlock(lockID); err != nil {\n    log.Printf(\"unlock failed (%v); attempting force-unlock of %s\", err, lockID)\n    if fuErr := sm.Unlock(lockID); fuErr != nil {\n        return fmt.Errorf(\"force-unlock also failed for %s: %w\", lockID, fuErr)\n    }\n}","preventionTips":["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."],"tags":["consul","locking","state","cleanup"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}