hashicorp/terraform · error
failed to lock state in Postgres: %s
Error message
failed to lock state in Postgres: %s
What it means
Thrown by Backend.StateMgr (pg/backend_state.go:97) during first-time workspace initialization. When the workspace does not yet exist, Terraform takes a Postgres advisory lock before writing the empty sentinel state; if stateMgr.Lock() returns an error it is wrapped here. The underlying cause is almost always lock contention (another session holds the advisory lock) or a DB connectivity fault.
Source
Thrown at internal/backend/remote-state/pg/backend_state.go:97
}
exists := false
for _, s := range existing {
if s == name {
exists = true
break
}
}
// 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 Workspaces() knows it exists.
if !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 Postgres: %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(`error unlocking Postgres state: %s`, err)
}
return parent
}
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 {
err = lockUnlock(err)
return nil, diags.Append(err)View on GitHub (pinned to c9def3e214)
Solutions
- Wait a few seconds and retry init — concurrent creation is the usual cause.
- Query pg_locks / pg_stat_activity for the offending advisory lock and backend, and pg_terminate_backend() it if it is stale.
- Verify Postgres connectivity and that the schema/states table are accessible.
- Serialize workspace creation in automation to avoid the race.
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check: is another session creating/locking this workspace? // SELECT 1 FROM pg_locks WHERE locktype='advisory' AND objid=-1; // SELECT 1 FROM pg_locks WHERE locktype='advisory' AND objid=<wsid>;
Try / catch
// stateMgr is the *remote.State wrapping the client; Lock errors come back as error
// lockID, err := stateMgr.Lock(info)
// if err != nil {
// if wait && isRetryableLock(err) { /* backoff + retry */ }
// return err
// } Prevention
- Serialize first-time workspace creation across CI pipelines.
- Ensure Terraform's DB connections are short-lived so killed runs release advisory locks.
- Monitor pg_locks for stale advisory locks on the states table.
When it happens
Trigger: Two `terraform init`/apply runs racing to create the same new workspace; a DB query/exec error during pg_try_advisory_lock; a long-lived DB session from a previous run still holding the advisory lock on the workspace id.
Common situations: Parallel CI pipelines bootstrapping the same workspace concurrently; a prior Terraform process was killed but its Postgres connection (pool/transaction) is still alive holding the advisory lock; flaky DB connection.
Related errors
- error unlocking Postgres state: %s
- Already locked for workspace creation: %s
- Workspace is already locked: %s
- Cannot lock workspace; already locked for workspace creation
- failed to lock azure state: %s
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/ac1f5089f75817ab.
Report an issue: GitHub.