{"record":{"id":"84cf92b4afbfd62e","repo":"hashicorp/terraform","slug":"v-additionally-unlocking-the-state-file-on-g","errorCode":null,"errorMessage":"%v\n\t\t\t\tAdditionally, unlocking the state file on Google Cloud Storage failed:\n\n\t\t\t\tError message: %q\n\t\t\t\tLock ID (gen): %v\n\t\t\t\tLock file URL: %v\n\n\t\t\t\tYou may have to force-unlock this state in order to use it again.\n\t\t\t\tThe GCloud backend acquires a lock during initialization to ensure\n\t\t\t\tthe initial state file is created.","messagePattern":"(.+?)\n\t\t\t\tAdditionally, unlocking the state file on Google Cloud Storage failed:\n\n\t\t\t\tError message: %q\n\t\t\t\tLock ID \\(gen\\): (.+?)\n\t\t\t\tLock file URL: (.+?)\n\n\t\t\t\tYou may have to force-unlock this state in order to use it again\\.\n\t\t\t\tThe GCloud backend acquires a lock during initialization to ensure\n\t\t\t\tthe initial state file is created\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/backend/remote-state/gcs/backend_state.go","lineNumber":136,"sourceCode":"\t\tlockID, err := st.Lock(lockInfo)\n\t\tif err != nil {\n\t\t\treturn nil, diags.Append(err)\n\t\t}\n\n\t\t// Local helper function so we can call it multiple places\n\t\tunlock := func(baseErr error) error {\n\t\t\tif err := st.Unlock(lockID); err != nil {\n\t\t\t\tconst unlockErrMsg = `%v\n\t\t\t\tAdditionally, unlocking the state file on Google Cloud Storage failed:\n\n\t\t\t\tError message: %q\n\t\t\t\tLock ID (gen): %v\n\t\t\t\tLock file URL: %v\n\n\t\t\t\tYou may have to force-unlock this state in order to use it again.\n\t\t\t\tThe GCloud backend acquires a lock during initialization to ensure\n\t\t\t\tthe initial state file is created.`\n\t\t\t\treturn fmt.Errorf(unlockErrMsg, baseErr, err.Error(), lockID, c.lockFileURL())\n\t\t\t}\n\n\t\t\treturn baseErr\n\t\t}\n\n\t\tif err := st.WriteState(states.NewState()); err != nil {\n\t\t\tunlockErr := unlock(err)\n\t\t\treturn nil, diags.Append(unlockErr)\n\t\t}\n\t\tif err := st.PersistState(nil); err != nil {\n\t\t\tunlockErr := unlock(err)\n\t\t\treturn nil, diags.Append(unlockErr)\n\t\t}\n\n\t\t// Unlock, the state should now be initialized\n\t\tif err := unlock(nil); err != nil {\n\t\t\treturn nil, diags.Append(err)\n\t\t}","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/backend/remote-state/gcs/backend_state.go#L118-L154","documentation":"StateMgr initializes a fresh state when none exists; to do so safely it locks first, then writes+persists, then unlocks. If WriteState or PersistState fails AND the subsequent st.Unlock(lockID) also fails, this composite message is returned: the original base error plus the unlock failure, the generated lock ID (GCS object generation number), and the lock file URL. The user is told they may need to force-unlock.","triggerScenarios":"During 'terraform init' on an empty workspace: the lock is acquired (a .tflock object is created with DoesNotExist precondition), then WriteState/PersistState fails (e.g. encryption key mismatch, bucket write permission revoked mid-flight), and the unlock delete targeting that generation also fails (concurrent modification, permission loss, transient GCS error).","commonSituations":"CSEK key was changed between lock and write; SA's storage.objects.delete was removed; transient GCS 5xx during the narrow init window; the .tflock was externally deleted/rewritten so the generation-match precondition on Unlock fails.","solutions":["Read the %q inner unlock message and the Lock ID: if the lock file still exists, run 'terraform force-unlock <Lock ID>' against the affected workspace.","Resolve the root baseErr (the %v at the start): e.g. fix encryption_key, restore write permissions, retry the init.","Verify the SA still holds storage.objects.create/delete on the bucket and prefix; confirm the .tflock object's current generation with 'gsutil stat gs://<bucket>/<prefix>/<ws>.tflock'.","Re-run 'terraform init' once the underlying cause is fixed; the next init will either reuse or overwrite the partially-initialized state."],"exampleFix":"# recover\nterraform workspace select <ws>\nterraform force-unlock 1234567890   # the Lock ID (gen) from the message\nterraform init","handlingStrategy":"try-catch","validationCode":"// Ensure CSEK and IAM are stable before init to avoid write-then-unlock-both-fail\nif err := validateCSEKAndIAM(); err != nil { return err }","typeGuard":null,"tryCatchPattern":"if diags := backend.StateMgr(ws); diags.HasErrors() {\n    msg := diags.Err().Error()\n    if strings.Contains(msg, \"force-unlock\") {\n        id := extractLockID(msg) // parse 'Lock ID (gen): N'\n        runTerraformForceUnlock(id)\n    }\n}","preventionTips":["Keep CSEK stable across init/apply; rotate via explicit re-encryption, never mid-init.","Ensure the SA holds storage.objects.create AND storage.objects.delete for the lock path.","Avoid concurrent inits on the same workspace."],"tags":["gcs","gcp","state-lock","force-unlock","init","encryption"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}