{"record":{"id":"ed1f1af77bcab29d","repo":"hashicorp/terraform","slug":"error-unlocking-oci-state-lock-id-s-error-s","errorCode":null,"errorMessage":"Error unlocking oci state. Lock ID: %s\n\nError: %s\n\nYou may have to force-unlock this state in order to use it again.","messagePattern":"Error unlocking oci state\\. Lock ID: (.+?)\n\nError: (.+?)\n\nYou may have to force-unlock this state in order to use it again\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/oci/backend_state.go","lineNumber":80,"sourceCode":"\t\t\texists = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\t// We need to create the object so it's listed by States.\n\tif !exists {\n\t\t// take a lock on this state while we write it\n\t\tlockInfo := statemgr.NewLockInfo()\n\t\tlockInfo.Operation = \"init\"\n\t\tlockId, err := b.client.Lock(lockInfo)\n\t\tif err != nil {\n\t\t\treturn nil, diags.Append(fmt.Errorf(\"failed to lock oci state: %s\", err))\n\t\t}\n\n\t\t// Local helper function so we can call it multiple places\n\t\tlockUnlock := func(parent error) error {\n\t\t\tif err := stateMgr.Unlock(lockId); err != nil {\n\t\t\t\treturn fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)\n\t\t\t}\n\t\t\treturn parent\n\t\t}\n\n\t\t// Grab the value\n\t\t// This is to ensure that no one beat us to writing a state between\n\t\t// the `exists` check and taking the lock.\n\t\tif err := stateMgr.RefreshState(); err != nil {\n\t\t\terr = lockUnlock(err)\n\t\t\treturn nil, diags.Append(err)\n\t\t}\n\n\t\t// If we have no state, we have to create an empty state\n\t\tif v := stateMgr.State(); v == nil {\n\t\t\tif err := stateMgr.WriteState(states.NewState()); err != nil {\n\t\t\t\terr = lockUnlock(err)\n\t\t\t\treturn nil, diags.Append(err)\n\t\t\t}","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/oci/backend_state.go#L62-L98","documentation":"Emitted by the inline lockUnlock closure (backend_state.go:78-83) using the errStateUnlock constant when stateMgr.Unlock fails inside the init-write flow. The lock was acquired but could not be released, so the workspace is left locked. The message deliberately includes the lock ID and tells the user to force-unlock.","triggerScenarios":"stateMgr.Unlock internally calls RemoteClient.Unlock; that fails if the lock file was concurrently deleted (etag mismatch / 404), if the lock ID no longer matches (314), if credentials/permissions changed mid-operation, or on a transient network error during the delete.","commonSituations":"A second user force-unlocked the lock between your Lock and Unlock; IAM permission was revoked mid-run; a network blip hit exactly the unlock call; manual deletion of the lock object during init.","solutions":["Copy the Lock ID from the message and run `terraform force-unlock <LOCK_ID>` after verifying no operation is active.","Inspect the bucket for the lock object; if it is already gone, the unlock effectively succeeded and you can proceed.","Stabilize IAM/network before retrying; an unlock that fails once will keep failing for the same reason until the underlying cause is fixed.","If force-unlock itself fails, delete the lock object directly in Object Storage."],"exampleFix":"# recovery from the printed message:\n# Error unlocking oci state. Lock ID: 2024-...\n# You may have to force-unlock this state in order to use it again.\nterraform force-unlock 2024-...","handlingStrategy":"fallback","validationCode":null,"typeGuard":"func isUnlockFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"Error unlocking oci state\")\n}","tryCatchPattern":"// After the init-write flow, if unlock fails, surface the lock ID for force-unlock\nif err := lockUnlock(nil); err != nil {\n    // The state is now locked; instruct the user to force-unlock\n    log.Printf(\"[WARN] state left locked: %v. Run `terraform force-unlock <ID>`\", err)\n    return err\n}","preventionTips":["Treat any unlock failure as requiring force-unlock — do not retry the same operation without resolving the lock.","Stabilize IAM/network before retrying so the unlock does not fail again.","Log lock IDs prominently so operators can recover quickly.","Audit concurrent force-unlock activity that could delete the lock out from under a running init."],"tags":["oci","state-locking","terraform","error-recovery"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}