hashicorp/terraform · error · statemgr.LockError
Already locked for workspace creation: %s
Error message
Already locked for workspace creation: %s
What it means
Returned by RemoteClient.Lock (pg/client.go:112) in the workspace-creation branch. When the SELECT finds no row (sql.ErrNoRows) Terraform tries to take the global creation advisory lock pg_try_advisory_lock(-1); if it returns false, another session is already creating a workspace, so a typed *statemgr.LockError with this message is returned.
Source
Thrown at internal/backend/remote-state/pg/client.go:112
return nil
}
// Try to acquire locks for the existing row `id` and the creation lock `-1`.
query := `SELECT %s.id, pg_try_advisory_lock(%s.id), pg_try_advisory_lock(-1) FROM %s.%s WHERE %s.name = $1`
row := c.Client.QueryRow(fmt.Sprintf(query, statesTableName, statesTableName, c.SchemaName, statesTableName, statesTableName), c.Name)
var pgLockId, didLock, didLockForCreate []byte
err = row.Scan(&pgLockId, &didLock, &didLockForCreate)
switch {
case err == sql.ErrNoRows:
// No rows means we're creating the workspace. Take the creation lock.
innerRow := c.Client.QueryRow(`SELECT pg_try_advisory_lock(-1)`)
var innerDidLock []byte
err := innerRow.Scan(&innerDidLock)
if err != nil {
return "", &statemgr.LockError{Info: info, Err: err}
}
if string(innerDidLock) == "false" {
return "", &statemgr.LockError{Info: info, Err: fmt.Errorf("Already locked for workspace creation: %s", c.Name)}
}
info.Path = "-1"
case err != nil:
return "", &statemgr.LockError{Info: info, Err: err}
case string(didLock) == "false":
// Existing workspace is already locked. Release the attempted creation lock.
lockUnlock("-1")
return "", &statemgr.LockError{Info: info, Err: fmt.Errorf("Workspace is already locked: %s", c.Name)}
case string(didLockForCreate) == "false":
// Someone has the creation lock already. Release the existing workspace because it might not be safe to touch.
lockUnlock(string(pgLockId))
return "", &statemgr.LockError{Info: info, Err: fmt.Errorf("Cannot lock workspace; already locked for workspace creation: %s", c.Name)}
default:
// Existing workspace is now locked. Release the attempted creation lock.
lockUnlock("-1")
info.Path = string(pgLockId)
}
c.info = infoView on GitHub (pinned to c9def3e214)
Solutions
- Retry after the other creation finishes — the -1 lock is short-lived.
- Find the holder: SELECT * FROM pg_locks WHERE locktype='advisory' AND objid=-1; join pg_stat_activity and pg_terminate_backend() if stale.
- Serialize workspace creation in your automation.
Defensive patterns
Strategy: try-catch
Validate before calling
// Before creating a workspace, check no one holds the creation lock // SELECT 1 FROM pg_locks WHERE locktype='advisory' AND classid=0 AND objid=-1;
Type guard
// RemoteClient.Lock returns a typed lock error you can narrow on
// var lockErr *statemgr.LockError
// if errors.As(err, &lockErr) {
// // lockErr.Info holds who currently holds the creation lock
// } Try / catch
// Creation-lock races are safe to retry after backoff
// for i := 0; i < 5; i++ {
// _, err := client.Lock(info)
// var le *statemgr.LockError
// if !errors.As(err, &le) || !strings.Contains(le.Error(), "workspace creation") {
// return err
// }
// time.Sleep(time.Duration(1<<i) * time.Second)
// } Prevention
- Serialize workspace creation in automation.
- Terminate stale backends holding advisory lock -1.
- Log lock acquisition so creation races are diagnosable.
When it happens
Trigger: Two clients concurrently creating the same (or any) new workspace — only one can hold the -1 creation lock at a time. Also fires if a previous create crashed but its DB connection still holds the -1 advisory lock.
Common situations: Parallel CI pipelines each bootstrapping a workspace; a team automation race; a prior Terraform run was OOM-killed leaving a live Postgres session holding -1.
Related errors
- Workspace is already locked: %s
- Cannot lock workspace; already locked for workspace creation
- failed to lock state in Postgres: %s
- %s (lock ID: "%s/%s")
- failed to lock azure state: %s
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/49208a86916c2f60.
Report an issue: GitHub.