{"record":{"id":"ac1f5089f75817ab","repo":"hashicorp/terraform","slug":"failed-to-lock-state-in-postgres-s","errorCode":null,"errorMessage":"failed to lock state in Postgres: %s","messagePattern":"failed to lock state in Postgres: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/pg/backend_state.go","lineNumber":97,"sourceCode":"\t}\n\n\texists := false\n\tfor _, s := range existing {\n\t\tif s == name {\n\t\t\texists = true\n\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)","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/pg/backend_state.go#L79-L115","documentation":"Thrown during Backend.StateMgr init when a workspace does not yet exist and stateMgr.Lock(lockInfo) returns an error while trying to take the initialization lock. The lock is needed to safely write an empty sentinel state so Workspaces() will list it. The %s wraps the underlying lock error (often a *statemgr.LockError from the pg client's advisory-lock logic).","triggerScenarios":"stateMgr.Lock(lockInfo) fails in the !exists branch of StateMgr. Causes: the pg advisory lock is already held (see errors 368-370), the DB connection was lost, or the state table/row is in an inconsistent state.","commonSituations":"Two concurrent `tofu init` runs for a new workspace; a previous init crashed holding the advisory lock; DB failover between Lock and the subsequent WriteState; insufficient DB privileges on the state table.","solutions":["Inspect the wrapped error - if it is 'Workspace is already locked' or 'Already locked for workspace creation', wait for the other process or run `tofu force-unlock`.","Verify the database connection (host, port, sslmode) and credentials in the backend config.","Re-run `tofu init` after the competing process completes.","Check the Postgres logs for advisory_lock contention or connection kills."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// preflight: ensure no advisory lock is already held for this workspace\nvar holder string\nerr := db.QueryRow(`SELECT mode FROM pg_locks WHERE locktype='advisory' AND objid=$1`, wsID).Scan(&holder)\nif err == nil { return fmt.Errorf(\"workspace %s already locked\", name) }","typeGuard":"func isAlreadyLockedErr(err error) bool {\n    return err != nil && (strings.Contains(err.Error(), \"already locked\") || strings.Contains(err.Error(), \"Workspace is already locked\"))\n}","tryCatchPattern":"lockId, err := stateMgr.Lock(lockInfo)\nif err != nil {\n    var le *statemgr.LockError\n    if errors.As(err, &le) { return fmt.Errorf(\"workspace busy, holder: %s; run force-unlock %s\", le.Info.Who, le.Info.ID) }\n    return err\n}","preventionTips":["Prevent concurrent inits on the same new workspace.","Surface *statemgr.LockError.Info so operators can identify the holder.","Keep the DB connection stable through apply (tune pool/keepalive)."],"tags":["postgres","state-lock","init","advisory-lock"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}