{"record":{"id":"9a56f6100ea77f87","repo":"hashicorp/terraform","slug":"invalid-lock-id","errorCode":null,"errorMessage":"invalid lock id","messagePattern":"invalid lock id","errorType":"validation","errorClass":"LockError","httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/inmem/backend.go","lineNumber":213,"sourceCode":"\treturn info.ID, nil\n}\n\nfunc (l *lockMap) unlock(name, id string) error {\n\tl.Lock()\n\tdefer l.Unlock()\n\n\tlockInfo := l.m[name]\n\n\tif lockInfo == nil {\n\t\treturn errors.New(\"state not locked\")\n\t}\n\n\tlockErr := &statemgr.LockError{\n\t\tInfo: &statemgr.LockInfo{},\n\t}\n\n\tif id != lockInfo.ID {\n\t\tlockErr.Err = errors.New(\"invalid lock id\")\n\t\t*lockErr.Info = *lockInfo\n\t\treturn lockErr\n\t}\n\n\tdelete(l.m, name)\n\treturn nil\n}\n","sourceCodeStart":195,"sourceCodeEnd":221,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/backend/remote-state/inmem/backend.go#L195-L221","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: hardcoded / wrong id\nsm.Unlock(\"deadbeef\")\n\n// after: capture and reuse the real id\nid, err := sm.Lock(info)\nif err != nil { return err }\ndefer sm.Unlock(id)","handlingStrategy":"validation","validationCode":"// Ensure the unlock id matches the one returned by Lock.\nif id != storedLockID {\n    return fmt.Errorf(\"refusing to unlock: provided id %q != held id %q\", id, storedLockID)\n}\nsm.Unlock(id)","typeGuard":null,"tryCatchPattern":"if err := sm.Unlock(id); err != nil {\n    var le *statemgr.LockError\n    if errors.As(err, &le) {\n        return fmt.Errorf(\"invalid lock id; held by %s, use 'terraform force-unlock %s'\", le.Info.ID, le.Info.ID)\n    }\n    return err\n}","preventionTips":["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."],"tags":["backend","inmem","locking","lock-id","testing"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}