{"record":{"id":"1c4953d4456dde37","repo":"hashicorp/terraform","slug":"error-unlocking-postgres-state-s","errorCode":null,"errorMessage":"error unlocking Postgres state: %s","messagePattern":"error unlocking Postgres state: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/pg/backend_state.go","lineNumber":103,"sourceCode":"\t\t\tbreak\n\t\t}\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 Workspaces() knows it exists.\n\tif !exists {\n\t\tlockInfo := statemgr.NewLockInfo()\n\t\tlockInfo.Operation = \"init\"\n\t\tlockId, err := stateMgr.Lock(lockInfo)\n\t\tif err != nil {\n\t\t\treturn nil, diags.Append(fmt.Errorf(\"failed to lock state in Postgres: %s\", err))\n\t\t}\n\n\t\t// Local helper function so we can call it multiple places\n\t\tlockUnlock := func(parent error) error {\n\t\t\tif err := stateMgr.Unlock(lockId); err != nil {\n\t\t\t\treturn fmt.Errorf(`error unlocking Postgres state: %s`, err)\n\t\t\t}\n\t\t\treturn parent\n\t\t}\n\n\t\tif v := stateMgr.State(); v == nil {\n\t\t\tif err := stateMgr.WriteState(states.NewState()); err != nil {\n\t\t\t\terr = lockUnlock(err)\n\t\t\t\treturn nil, diags.Append(err)\n\t\t\t}\n\t\t\tif err := stateMgr.PersistState(nil); err != nil {\n\t\t\t\terr = lockUnlock(err)\n\t\t\t\treturn nil, diags.Append(err)\n\t\t\t}\n\t\t}\n\n\t\t// Unlock, the state should now be initialized\n\t\tif err := lockUnlock(nil); err != nil {\n\t\t\treturn nil, diags.Append(err)","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/pg/backend_state.go#L85-L121","documentation":"Wrapped by the lockUnlock helper inside Backend.StateMgr when stateMgr.Unlock(lockId) fails after the init lock was acquired but a later step (WriteState/PersistState/Refresh) errored. This is cleanup-path noise: the original error is chained as parent, and the unlock failure is surfaced alongside it. It means the advisory lock may still be held after the run aborted.","triggerScenarios":"stateMgr.Unlock(lockId) returns err != nil inside the lockUnlock closure, which is called from RefreshState/WriteState/PersistState error branches. Causes: DB connection dropped between acquiring the advisory lock and releasing it, the lock row was deleted out-of-band, or pg_advisory_unlock returned false/errored.","commonSituations":"Postgres connection timeout during apply; DB restarted mid-run; another admin manually released the advisory lock; connection pool evicted the session that held the session-level advisory lock.","solutions":["Run `tofu force-unlock <lock-id>` to clear the stuck advisory lock (or SELECT pg_advisory_unlock(<id>) from a psql session if the holding session is gone).","Check Postgres for idle-in-transaction sessions holding the lock: `SELECT * FROM pg_locks WHERE locktype='advisory';`.","Re-run `tofu init` then `tofu apply` once the lock is cleared.","Ensure the DB connection has keepalive/idle timeout settings that do not kill sessions mid-apply."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// ensure the holding session is alive; advisory locks are session-scoped\n// run `SELECT pg_advisory_unlock(<id>)` from the SAME session that locked","typeGuard":null,"tryCatchPattern":"err = lockUnlock(parentErr)\nif err != nil {\n    log.Printf(\"WARN unlock failed; manual `tofu force-unlock %s` may be required: %v\", lockId, err)\n    return parentErr\n}","preventionTips":["Do not let the DB kill idle sessions mid-apply (raise idle timeout).","Always report the lock ID in unlock-failure logs so operators can force-unlock.","Monitor pg_locks for advisory locks from killed sessions."],"tags":["postgres","state-lock","unlock","cleanup","advisory-lock"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}