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

  1. Read the combined message: note the Lock ID (gen) and Lock file URL, then run `terraform force-unlock <lock-id>`.
  2. Investigate the base error (first %v) — that is the original WriteState/PersistState failure.
  3. Grant the service account full read/write/delete on the bucket and lock file path.
  4. 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

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


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)