{"record":{"id":"bf308b322a5b526d","repo":"hashicorp/terraform","slug":"lock-id-does-not-match-existing-lock-bf308b","errorCode":null,"errorMessage":"lock ID does not match existing lock","messagePattern":"lock ID does not match existing lock","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloud/state.go","lineNumber":475,"sourceCode":"\t\treturn nil\n\t}\n\n\tctx := context.Background()\n\n\t// We first check if there was an error while uploading the latest\n\t// state. If so, we will not unlock the workspace to prevent any\n\t// changes from being applied until the correct state is uploaded.\n\tif s.stateUploadErr {\n\t\treturn nil\n\t}\n\n\tlockErr := &statemgr.LockError{Info: s.lockInfo}\n\n\t// With lock info this should be treated as a normal unlock.\n\tif s.lockInfo != nil {\n\t\t// Verify the expected lock ID.\n\t\tif s.lockInfo.ID != id {\n\t\t\tlockErr.Err = fmt.Errorf(\"lock ID does not match existing lock\")\n\t\t\treturn lockErr\n\t\t}\n\n\t\t// Unlock the workspace.\n\t\terr := RetryBackoff(ctx, func() error {\n\t\t\t_, err := s.tfeClient.Workspaces.Unlock(ctx, s.workspace.ID)\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, tfe.ErrWorkspaceLockedStateVersionStillPending) {\n\t\t\t\t\t// This is a retryable error.\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t// This will not be retried\n\t\t\t\treturn &errorUnlockFailed{innerError: err}\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\n\t\tif err != nil {","sourceCodeStart":457,"sourceCodeEnd":493,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/cloud/state.go#L457-L493","documentation":"Thrown during Unlock when s.lockInfo is non-nil (a normal lock was acquired earlier in this process) but the caller-supplied id does not match s.lockInfo.ID. This is a safety check to prevent one process from unlocking a lock held by a different process. The error is wrapped in a statemgr.LockError carrying the original lock info.","triggerScenarios":"The statemgr framework calls Unlock with a different ID than the one returned by Lock; manual or programmatic invocation of Unlock with a stale or mismatched ID; a state manager instance is reused across multiple lock/unlock cycles without resetting lockInfo; two concurrent goroutines sharing the same State instance racing on lock/unlock.","commonSituations":"Custom code wrapping statemgr that loses or rewrites the lock ID between Lock and Unlock; testing harness that mocks lock IDs inconsistently; rarely seen by end users since the statemgr framework manages IDs internally.","solutions":["Ensure the id passed to Unlock is exactly the string returned by the corresponding Lock call","Do not share a single State instance across concurrent operations that lock/unlock independently","If using force-unlock semantics, pass the org/workspace ID format instead and ensure s.lockInfo is nil"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before Unlock, verify the ID matches what Lock returned:\nif lockID != acquiredLockID {\n    return fmt.Errorf(\"refusing to unlock: provided ID %q does not match acquired lock ID %q\", lockID, acquiredLockID)\n}","typeGuard":null,"tryCatchPattern":"err := stateMgr.Unlock(lockID)\nif err != nil {\n    var lockErr *statemgr.LockError\n    if errors.As(err, &lockErr) && strings.Contains(err.Error(), \"does not match\") {\n        // ID mismatch — do not force; investigate the lock holder\n        return fmt.Errorf(\"unlock refused due to ID mismatch: %w\", err)\n    }\n    return err\n}","preventionTips":["Always store the exact string returned by Lock and pass it unmodified to Unlock","Never reuse a State instance across independent lock/unlock cycles without re-locking","In custom statemgr wrappers, propagate the lock ID through a dedicated field, not derived/computed values"],"tags":["locking","state-unlock","internal","terraform"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}