hashicorp/terraform · error

failed to retrieve lock info

Error message

failed to retrieve lock info: %s

What it means

Thrown by RemoteClient.Unlock at the start of unlock: getLockInfo() failed before any deletion is attempted. The unlock cannot proceed because it cannot verify which lock is held; the error is wrapped in a statemgr.LockError whose Info is left nil.

Solutions

  1. Verify the OTS table exists and the lock row is present.
  2. Grant ots:GetRow and ots:DeleteRow on the table.
  3. If the lock row was already removed, the unlock error is benign — confirm no lock row remains and proceed.
  4. Retry the terraform command once OTS is reachable.
Defensive patterns

Strategy: try-catch

Try / catch

// In Unlock, if getLockInfo fails, treat already-absent row as success; otherwise surface LockError.
info, err := c.getLockInfo()
if err != nil {
    if isRowNotExist(err) { return nil } // lock already gone — nothing to unlock
    return &statemgr.LockError{Err: fmt.Errorf("failed to retrieve lock info: %s", err)}
}

Prevention

When it happens

Trigger: Calling Unlock(id) and getLockInfo() errors — OTS GetRow on the lock path failed: table missing, credentials lack ots:GetRow, endpoint unreachable, or the lock row was already removed out-of-band.

Common situations: A stale lock was manually deleted before terraform tried to unlock; OTS permissions changed mid-run; OTS regional issue; lock row primary key shape mismatch.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/35d4fd66e3640cb7. Report an issue: GitHub.

Appendix: source

Thrown at internal/backend/remote-state/oss/client.go:373

		infoData = v[0].Value.(string)
	}
	lockInfo := &statemgr.LockInfo{}
	err = json.Unmarshal([]byte(infoData), lockInfo)
	if err != nil {
		return nil, err
	}
	return lockInfo, nil
}
func (c *RemoteClient) Unlock(id string) error {
	if c.otsTable == "" {
		return nil
	}

	lockErr := &statemgr.LockError{}

	lockInfo, err := c.getLockInfo()
	if err != nil {
		lockErr.Err = fmt.Errorf("failed to retrieve lock info: %s", err)
		return lockErr
	}
	lockErr.Info = lockInfo

	if lockInfo.ID != id {
		lockErr.Err = fmt.Errorf("lock id %q does not match existing lock", id)
		return lockErr
	}
	params := &tablestore.DeleteRowRequest{
		DeleteRowChange: &tablestore.DeleteRowChange{
			TableName: c.otsTable,
			PrimaryKey: &tablestore.PrimaryKey{
				PrimaryKeys: []*tablestore.PrimaryKeyColumn{
					{
						ColumnName: pkName,
						Value:      c.lockPath(),
					},
				},

View on GitHub (pinned to d32a084675)