hashicorp/terraform · error · statemgr.LockError

failed to retrieve lock info: %s

Error message

failed to retrieve lock info: %s

What it means

In RemoteClient.Unlock() (client.go:371-373), c.getLockInfo() failed before the delete. getLockInfo does a GetRow on the OTS lock row and json.Unmarshal's the Info column; failure here means the lock row cannot be read. Unlock returns a statemgr.LockError with this as Err and nil Info.

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 c9def3e214)

Solutions

  1. Grant tablestore:GetRow on the table.
  2. Inspect the OTS row; if its Info JSON is corrupt, force-unlock by deleting the row directly.
  3. Ensure no external tool writes to the lock table's Info column.
  4. Recreate the OTS table if it was removed.

Example fix

# before: cannot read lock row
terraform force-unlock <id>   # also fails on getLockInfo

# fix: delete the OTS row directly in the console (LockID = <bucket>/<stateFile>),
# then re-run terraform init.
Defensive patterns

Strategy: fallback

Validate before calling

// Validate that the lock row's Info column unmarshals before attempting Unlock.
func lockInfoReadable(c *tablestore.TableStoreClient, table, path string) (*statemgr.LockInfo, error) {
    // reuse getLockInfo logic; return error if Info missing/corrupt
    return info, nil
}

Try / catch

// If getLockInfo fails, fall back to direct OTS row deletion (force-unlock by hand).
if strings.Contains(err.Error(), "failed to retrieve lock info") {
    deleteOTSLockRowManually(table, path)
}

Prevention

When it happens

Trigger: GetRow fails (tablestore:GetRow denied, table missing, OTS error) OR the Info column JSON fails to unmarshal into statemgr.LockInfo (corrupted/edited row). Either way the unlock cannot proceed because the code must first verify the lock ID matches (line 378).

Common situations: Lock row Info column manually edited or written by a non-terraform tool; GetRow permission missing; OTS table dropped; the lock row's Info value is empty/non-JSON.

Related errors


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