hashicorp/terraform · error
Additionally, unlocking the state file on Google Cloud…
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
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.
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.
Example fix
// recovery $ terraform force-unlock 1234567890 # then re-run terraform init && terraform apply
Defensive patterns
Strategy: fallback
Validate before calling
// Pre-acquire lock only when write perms are confirmed; verify KMS/bucket access first.
Try / catch
// Treat the combined error as recoverable via force-unlock.
if err := backend.Configure(...); err != nil {
if isLockHeld(err) {
log.Print(err) // contains Lock ID + URL
return fmt.Errorf("run terraform force-unlock <id>")
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Lock ID should be numerical value, got
- Error unlocking Consul state. Lock ID
- Unlocking the state file on TencentCloud cos backend…
- writing failed
- cowardly refusing to delete the
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/84cf92b4afbfd62e.
Report an issue: GitHub.
Appendix: 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 d32a084675)