hashicorp/terraform · error · statemgr.LockError
lock id %q does not match existing lock
Error message
lock id %q does not match existing lock
What it means
In RemoteClient.Unlock() (client.go:378-379), the lock row was read successfully but lockInfo.ID != id - the caller-supplied lock ID does not match the stored one. Unlock refuses to delete someone else's lock. This is a deliberate safety check returned as a statemgr.LockError.
Source
Thrown at internal/backend/remote-state/oss/client.go:379
}
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(),
},
},
},
Condition: &tablestore.RowCondition{
RowExistenceExpectation: tablestore.RowExistenceExpectation_IGNORE,
},
},
}View on GitHub (pinned to c9def3e214)
Solutions
- Re-read the current lock info (terraform force-unlock prints the held ID, or check the OTS Info column) and use that ID.
- Confirm no other process re-acquired the lock after you recorded the ID.
- If certain the held lock is stale and yours, use force-unlock with -force if available, or delete the OTS row manually.
Example fix
# before: wrong id terraform force-unlock 11111111-... # error: lock id "11111111-..." does not match existing lock # after: use the actual held id (from terraform's lock message or OTS row) terraform force-unlock 22222222-...
Defensive patterns
Strategy: validation
Validate before calling
// Before Unlock, fetch the held lock ID and compare; refuse to proceed if mismatched.
func unlockSafe(heldID, suppliedID string) error {
if heldID != suppliedID {
return fmt.Errorf("supplied %q != held %q; not your lock", suppliedID, heldID)
}
return nil
} Try / catch
// On mismatch, re-read the current held ID and retry force-unlock with the right one.
if strings.Contains(err.Error(), "does not match existing lock") {
id := readCurrentLockID()
return terraformForceUnlock(id)
} Prevention
- Always copy the full lock ID verbatim from terraform's own message.
- Re-check the held lock ID before force-unlock if time has passed.
- Avoid running concurrent force-unlocks that could re-lock under your nose.
When it happens
Trigger: Unlock(id) where id differs from the Info.ID in the OTS lock row. Happens with an outdated/stale lock ID, a workspace re-locked by another process, or a hand-copied wrong ID.
Common situations: User force-unlocks with an old lock ID from a prior run; another CI job re-locked the workspace; lock ID truncated/typo'd when passed to terraform force-unlock.
Related errors
- failed to retrieve lock info: %s
- error describing table store %s: %#v
- failed to lock OSS state: %s
- Error unlocking Alibaba Cloud OSS state file: Lock ID: %s E
- invoking PutRow got an error: %#v
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/cec5d8fab78d21b8.
Report an issue: GitHub.