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

  1. Use the exact lock ID returned by Lock when calling Unlock.
  2. If the lock is stale, use the documented force-unlock flow with the correct ID.
  3. 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

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


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