hashicorp/terraform · critical
%v Additionally, unlocking the state file on Google Clou
Error message
%v Additionally, unlocking the state file on Google Cloud Storage failed: Error message: %q Lock ID (gen): %v Lock file URL: %v You may have to force-unlock this state in order to use it again. The GCloud backend acquires a lock during initialization to ensure the initial state file is created.
What it means
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.
Source
Thrown at internal/backend/remote-state/gcs/backend_state.go:136
lockID, err := st.Lock(lockInfo)
if err != nil {
return nil, diags.Append(err)
}
// Local helper function so we can call it multiple places
unlock := func(baseErr error) error {
if err := st.Unlock(lockID); err != nil {
const unlockErrMsg = `%v
Additionally, unlocking the state file on Google Cloud Storage failed:
Error message: %q
Lock ID (gen): %v
Lock file URL: %v
You may have to force-unlock this state in order to use it again.
The GCloud backend acquires a lock during initialization to ensure
the initial state file is created.`
return fmt.Errorf(unlockErrMsg, baseErr, err.Error(), lockID, c.lockFileURL())
}
return baseErr
}
if err := st.WriteState(states.NewState()); err != nil {
unlockErr := unlock(err)
return nil, diags.Append(unlockErr)
}
if err := st.PersistState(nil); err != nil {
unlockErr := unlock(err)
return nil, diags.Append(unlockErr)
}
// Unlock, the state should now be initialized
if err := unlock(nil); err != nil {
return nil, diags.Append(err)
}View on GitHub (pinned to c9def3e214)
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.
Example fix
# recover terraform workspace select <ws> terraform force-unlock 1234567890 # the Lock ID (gen) from the message terraform init
Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure CSEK and IAM are stable before init to avoid write-then-unlock-both-fail
if err := validateCSEKAndIAM(); err != nil { return err } Try / catch
if diags := backend.StateMgr(ws); diags.HasErrors() {
msg := diags.Err().Error()
if strings.Contains(msg, "force-unlock") {
id := extractLockID(msg) // parse 'Lock ID (gen): N'
runTerraformForceUnlock(id)
}
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- writing %q failed: %v
- Lock ID should be numerical value, got '%s'
- Error loading encryption key: %s
- Error decoding encryption key: %s
- Failed to open state file at %v: %v
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/84cf92b4afbfd62e.
Report an issue: GitHub.