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
- Verify the OTS table exists and the lock row is present.
- Grant ots:GetRow and ots:DeleteRow on the table.
- If the lock row was already removed, the unlock error is benign — confirm no lock row remains and proceed.
- 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
- Grant ots:GetRow and ots:DeleteRow on the lock table for the lifetime of the run.
- Avoid manually deleting lock rows out-of-band; use 'terraform force-unlock'.
- Confirm OTS table existence before running operations that unlock.
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
- error describing table store
- Error unlocking Alibaba Cloud OSS state file: Lock ID
- failed to lock OSS state
- failed to store state MD5
- getting lock info got an error: %#v
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)