{"record":{"id":"e2303acc2aef9765","repo":"hashicorp/terraform","slug":"failed-to-lock-state-in-consul-s","errorCode":null,"errorMessage":"failed to lock state in Consul: %s","messagePattern":"failed to lock state in Consul: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/consul/backend_state.go","lineNumber":108,"sourceCode":"\t}\n\n\tif !b.lock {\n\t\tstateMgr.DisableLocks()\n\t}\n\n\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","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/backend/remote-state/consul/backend_state.go#L90-L126","documentation":"For non-default workspaces, StateMgr (backend_state.go:108) acquires a Consul lock to initialize an empty-state sentinel. If stateMgr.Lock fails (lock already held, Consul unreachable, session creation failure), the backend cannot proceed and returns this with the underlying lock error.","triggerScenarios":"Initializing a non-default Consul state workspace while another terraform process holds the lock, the Consul agent is down, or session creation/lock acquisition failed.","commonSituations":"Concurrent terraform runs against the same workspace; a stale lock left by a crashed run; Consul connectivity or ACL issues.","solutions":["Wait for the other run to finish, or run `terraform force-unlock <id>` with the lock ID.","Confirm the Consul agent is reachable and healthy.","Reduce concurrent runs on the same workspace; enable locking only when needed."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"# confirm Consul is reachable and check for an existing lock entry\nconsul members >/dev/null 2>&1 || { echo \"consul unreachable\" >&2; exit 1; }\nconsul kv get -recurse tfstate/ | grep -q '/.lockinfo' \\\n  && echo \"WARN: a lock info entry exists; another run may hold the lock\"","typeGuard":null,"tryCatchPattern":"// retry StateMgr init on lock contention, then surface the lock error\nvar sm statemgr.Full\nerr := backoff.RetryNotify(func() error {\n    var d tfdiags.Diagnostics\n    sm, d = b.StateMgr(ws)\n    return d.Err()\n}, backoff.NewExponentialBackOff(), func(e error, d time.Duration) {\n    log.Printf(\"consul lock busy, retrying in %s: %v\", d, e)\n})","preventionTips":["Avoid concurrent terraform runs against the same workspace.","Clear stale locks with force-unlock after a crash.","Monitor Consul health before running."],"tags":["consul","locking","state","concurrency"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T20:17:04.800Z"}