{"record":{"id":"1e6c2b547509727c","repo":"opentofu/opentofu","slug":"failed-to-unlock-state-s","errorCode":null,"errorMessage":"Failed to unlock state: %s","messagePattern":"Failed to unlock state: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/command/unlock.go","lineNumber":141,"sourceCode":"\t\t\t\"may still be in use. Only 'yes' will be accepted to confirm.\"\n\n\t\tv, err := c.UIInput().Input(context.Background(), &tofu.InputOpts{\n\t\t\tId:          \"force-unlock\",\n\t\t\tQuery:       \"Do you really want to force-unlock?\",\n\t\t\tDescription: desc,\n\t\t})\n\t\tif err != nil {\n\t\t\tview.Diagnostics(diags.Append(fmt.Errorf(\"Error asking for confirmation: %s\", err)))\n\t\t\treturn 1\n\t\t}\n\t\tif v != \"yes\" {\n\t\t\tview.ForceUnlockCancelled()\n\t\t\treturn 1\n\t\t}\n\t}\n\n\tif err := stateMgr.Unlock(context.TODO(), lockID); err != nil {\n\t\tview.Diagnostics(diags.Append(fmt.Errorf(\"Failed to unlock state: %s\", err)))\n\t\treturn 1\n\t}\n\tview.ForceUnlockSucceeded()\n\treturn 0\n}\n\nfunc (c *UnlockCommand) Help() string {\n\thelpText := `\nUsage: tofu [global options] force-unlock [options] LOCK_ID\n\n  Manually unlock the state for the defined configuration.\n\n  This will not modify your infrastructure. This command removes the lock on the\n  state for the current workspace. The behavior of this lock is dependent\n  on the backend being used. Local state files cannot be unlocked by another\n  process.\n\nOptions:","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/opentofu/opentofu/blob/3561785c48c1ce615e7c50261bd351f26053efa2/internal/command/unlock.go#L123-L159","documentation":"After confirmation, force-unlock calls stateMgr.Unlock with the given lock ID. The wrapped error comes from the backend: most commonly the ID does not match the currently held lock (stale or wrong ID), the lock was already released, or the process lacks permission to modify the lock entry (e.g. DynamoDB UpdateItem on the lock table).","triggerScenarios":"Passing a lock ID from an older failure after the lock was already released or superseded; unlocking a workspace other than the one holding the lock; missing write permission on the lock entry; corrupted lock record in the lock table.","commonSituations":"Copy-pasting yesterday's lock ID; multiple lock/unlock attempts racing; environments sharing a lock table with mismatched keys.","solutions":["Get the lock ID from the most recent locking error message, not an older one","Verify you target the same backend and workspace that holds the lock (tofu workspace show)","If the backend reports the lock is already unlocked, nothing is wrong — run a plan to confirm access works","Check write permission on the lock entry (e.g. dynamodb:UpdateItem on the lock table item)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// verify the lock is live and its ID matches before unlocking\nout, err := exec.Command(\"tofu\", \"plan\", \"-lock=false\", \"-input=false\", \"-detailed-exitcode\").CombinedOutput()\nif err != nil {\n\tif strings.Contains(string(out), lockID) {\n\t\t// lock ID confirmed current: safe to unlock\n\t} else {\n\t\treturn errors.New(\"lock ID stale — take the ID from this latest error instead\")\n\t}\n}","typeGuard":null,"tryCatchPattern":"if err := stateMgr.Unlock(context.TODO(), lockID); err != nil {\n\tmsg := err.Error()\n\tswitch {\n\tcase strings.Contains(msg, \"already unlocked\"), strings.Contains(msg, \"lock not found\"):\n\t\treturn nil // nothing to do\n\tcase strings.Contains(msg, \"does not match\"), strings.Contains(msg, \"different ID\"):\n\t\treturn fmt.Errorf(\"stale lock ID — copy the ID from the latest locking error: %s\", msg)\n\tdefault:\n\t\treturn err\n\t}\n}","preventionTips":["Always copy the lock ID from the most recent failed operation's error output","Confirm workspace and backend match the run that took the lock before unlocking","Verify write permission on the lock table/entry as part of unlock runbooks"],"tags":["go","opentofu","locking","state","backend"],"backgroundTag":null,"analyzedSha":"3561785c48c1ce615e7c50261bd351f26053efa2","analyzedAt":"2026-08-15T23:27:16.226Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}