hashicorp/terraform · error

failed to lock azure state: %s

Error message

failed to lock azure state: %s

What it means

Raised in Backend.StateMgr (backend_state.go:117-119) when initializing a brand-new workspace state blob. Because the blob does not yet exist, Terraform acquires a lease (client.Lock) to safely create it; if Lock returns an error (another process already holds the lease, or the lease acquire data-plane call failed), this error wraps the resulting statemgr.LockError which also carries the existing lock info.

Source

Thrown at internal/backend/remote-state/azure/backend_state.go:119

		accountName:        b.accountName,
		snapshot:           b.snapshot,
	}

	stateMgr := &remote.State{Client: client}

	// Grab the value
	if err := stateMgr.RefreshState(); err != nil {
		return nil, diags.Append(err)
	}
	//if this isn't the default state name, we need to create the object so
	//it's listed by States.
	if v := stateMgr.State(); v == nil {
		// take a lock on this state while we write it
		lockInfo := statemgr.NewLockInfo()
		lockInfo.Operation = "init"
		lockId, err := client.Lock(lockInfo)
		if err != nil {
			return nil, diags.Append(fmt.Errorf("failed to lock azure state: %s", err))
		}

		// Local helper function so we can call it multiple places
		lockUnlock := func(parent error) error {
			if err := stateMgr.Unlock(lockId); err != nil {
				return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)
			}
			return parent
		}

		// Grab the value
		if err := stateMgr.RefreshState(); err != nil {
			err = lockUnlock(err)
			return nil, diags.Append(err)
		}
		//if this isn't the default state name, we need to create the object so
		//it's listed by States.
		if v := stateMgr.State(); v == nil {

View on GitHub (pinned to c9def3e214)

Solutions

  1. Wait for the other Terraform process to finish and release the lease, then retry
  2. Identify the lock holder and run 'terraform force-unlock <lock-id>' with the ID from the error message
  3. Use the Azure portal / az storage blob lease break to release a stale lease: az storage blob lease break --account-name <account> -c <container> -b <key>
  4. Ensure only one writer runs per workspace (CI queue/locking) to avoid contention

Example fix

# the error reports the existing lock info; force-unlock with its id
terraform force-unlock a1b2c3d4-e5f6-7890-abcd-ef1234567890

# or break the lease directly in Azure
az storage blob lease break \
  --account-name mystage --auth-mode login \
  -c tfstate -b "prod.terraform.tfstate"
Defensive patterns

Strategy: retry

Validate before calling

# Before opening a fresh workspace, check whether its state blob is lease-locked
KEY="${ARM_KEY}env:${WS_NAME}"
STATUS=$(az storage blob show --account-name "$ARM_STORAGE_ACCOUNT_NAME" -c "$ARM_CONTAINER_NAME" -n "$KEY" --query 'properties.lease.status' -o tsv 2>/dev/null)
[ "$STATUS" = "locked" ] && echo "WARN: blob already locked -> error 151 likely; wait or force-unlock" || echo "OK: not locked"

Try / catch

# Bash: on lock failure, surface the lock id and offer force-unlock
run_with_lock_guard() {
  if ! terraform "$@" 2>err.log; then
    if grep -q "failed to lock azure state" err.log; then
      LID=$(grep -oE '[0-9a-f-]{36}' err.log | head -1)
      echo "State locked. Review holder, then: terraform force-unlock $LID"
    fi
    return 1
  fi
}

Prevention

When it happens

Trigger: Produced at backend_state.go:117-119 the first time a non-existent workspace state is opened (e.g. 'terraform workspace new' or 'terraform init' for a fresh key) when client.Lock fails — typically because properties.LeaseStatus == Locked (error 154) or AcquireLease failed.

Common situations: Two concurrent 'terraform apply' runs on the same new workspace; a previous run crashed holding the lease; CI retried a job while the first was still running; a stale lease left by a terminated process.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/e05ee4e97ff44011. Report an issue: GitHub.