hashicorp/terraform · error · LockError
invalid lock id
Error message
invalid lock id
What it means
Returned by lockMap.unlock (internal/backend/remote-state/inmem/backend.go:213) wrapped in a statemgr.LockError when the provided id does not match lockInfo.ID for the named state. The inmem backend stores a LockInfo per state; unlock requires the exact lock ID that Lock returned to prevent one caller from releasing another's lock.
Source
Thrown at internal/backend/remote-state/inmem/backend.go:213
return info.ID, nil
}
func (l *lockMap) unlock(name, id string) error {
l.Lock()
defer l.Unlock()
lockInfo := l.m[name]
if lockInfo == nil {
return errors.New("state not locked")
}
lockErr := &statemgr.LockError{
Info: &statemgr.LockInfo{},
}
if id != lockInfo.ID {
lockErr.Err = errors.New("invalid lock id")
*lockErr.Info = *lockInfo
return lockErr
}
delete(l.m, name)
return nil
}
View on GitHub (pinned to c9def3e214)
Solutions
- Use the exact lock ID returned by Lock when calling Unlock.
- If the lock is stale, use the documented force-unlock flow with the correct ID.
- In tests, thread the lock ID through rather than hardcoding it.
Example fix
// before: hardcoded / wrong id
sm.Unlock("deadbeef")
// after: capture and reuse the real id
id, err := sm.Lock(info)
if err != nil { return err }
defer sm.Unlock(id) Defensive patterns
Strategy: validation
Validate before calling
// Ensure the unlock id matches the one returned by Lock.
if id != storedLockID {
return fmt.Errorf("refusing to unlock: provided id %q != held id %q", id, storedLockID)
}
sm.Unlock(id) Try / catch
if err := sm.Unlock(id); err != nil {
var le *statemgr.LockError
if errors.As(err, &le) {
return fmt.Errorf("invalid lock id; held by %s, use 'terraform force-unlock %s'", le.Info.ID, le.Info.ID)
}
return err
} Prevention
- Always thread the lock ID returned by Lock through to Unlock; never hardcode it.
- For force-unlock, retrieve and present the actual held ID to the user.
- In tests, store lock IDs in variables rather than literals.
When it happens
Trigger: Calling Unlock with a stale, hardcoded, or wrong lock ID; passing the lock ID of a different operation; a second operation trying to force-unlock using an ID it guessed/found rather than the one returned by Lock.
Common situations: Hardcoding lock IDs in tests; copying a lock ID from logs of a previous run; concurrent operations where one captures the other's ID; 'terraform force-unlock <wrong-id>' against the inmem backend.
Related errors
- state locked
- state not locked
- failed to lock inmem state: %s
- consul lock was lost
- the state is already locked by another terraform client
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/9a56f6100ea77f87.
Report an issue: GitHub.