{"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":"exception","errorClass":null,"httpStatus":null,"severity":"error","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/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/gcs/backend_state.go#L118-L154","documentation":"Composed by the unlock closure during initial state creation when the primary operation (WriteState or PersistState) fails AND the subsequent st.Unlock(lockID) also fails. The message concatenates the base error, the unlock error, the lock generation ID, and the lock file URL so the user can force-unlock.","triggerScenarios":"During initial-state bootstrap the backend acquires a lock, then st.WriteState(NewState()) or st.PersistState(nil) errors; the unlock closure then calls st.Unlock(lockID) which itself returns an error. The combined message is returned via diags.Append.","commonSituations":"Transient GCS write failure during state creation followed by a concurrent lock release race; partial-permission service account that can lock but not write the state object; bucket quota exceeded; lock file generation mismatch caused by another process holding the lock.","solutions":["Read the combined message: note the Lock ID (gen) and Lock file URL, then run `terraform force-unlock <lock-id>`.","Investigate the base error (first %v) — that is the original WriteState/PersistState failure.","Grant the service account full read/write/delete on the bucket and lock file path.","Retry after the force-unlock; if it recurs, check bucket quotas and concurrent CI runs."],"exampleFix":"// recovery\n$ terraform force-unlock 1234567890\n# then re-run\nterraform init && terraform apply","handlingStrategy":"fallback","validationCode":"// Pre-acquire lock only when write perms are confirmed; verify KMS/bucket access first.","typeGuard":null,"tryCatchPattern":"// Treat the combined error as recoverable via force-unlock.\nif err := backend.Configure(...); err != nil {\n    if isLockHeld(err) {\n        log.Print(err) // contains Lock ID + URL\n        return fmt.Errorf(\"run terraform force-unlock <id>\")\n    }\n}","preventionTips":["Ensure the service account has full objectAdmin before bootstrap.","Surface the combined error message to operators so they can force-unlock.","Eliminate concurrent initializations against the same state."],"tags":["gcs","backend","state-locking","recovery","initialization"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}