hashicorp/terraform · error

failed to lock state in Consul: %s

Error message

failed to lock state in Consul: %s

What it means

For non-default workspaces, StateMgr (backend_state.go:108) acquires a Consul lock to initialize an empty-state sentinel. If stateMgr.Lock fails (lock already held, Consul unreachable, session creation failure), the backend cannot proceed and returns this with the underlying lock error.

Source

Thrown at internal/backend/remote-state/consul/backend_state.go:108

	}

	if !b.lock {
		stateMgr.DisableLocks()
	}

	// the default state always exists
	if name == backend.DefaultStateName {
		return stateMgr, nil
	}

	// Grab a lock, we use this to write an empty state if one doesn't
	// exist already. We have to write an empty state as a sentinel value
	// so States() knows it exists.
	lockInfo := statemgr.NewLockInfo()
	lockInfo.Operation = "init"
	lockId, err := stateMgr.Lock(lockInfo)
	if err != nil {
		return nil, diags.Append(fmt.Errorf("failed to lock state in Consul: %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 we have no state, we have to create an empty state

View on GitHub (pinned to c9def3e214)

Solutions

  1. Wait for the other run to finish, or run `terraform force-unlock <id>` with the lock ID.
  2. Confirm the Consul agent is reachable and healthy.
  3. Reduce concurrent runs on the same workspace; enable locking only when needed.
Defensive patterns

Strategy: retry

Validate before calling

# confirm Consul is reachable and check for an existing lock entry
consul members >/dev/null 2>&1 || { echo "consul unreachable" >&2; exit 1; }
consul kv get -recurse tfstate/ | grep -q '/.lockinfo' \
  && echo "WARN: a lock info entry exists; another run may hold the lock"

Try / catch

// retry StateMgr init on lock contention, then surface the lock error
var sm statemgr.Full
err := backoff.RetryNotify(func() error {
    var d tfdiags.Diagnostics
    sm, d = b.StateMgr(ws)
    return d.Err()
}, backoff.NewExponentialBackOff(), func(e error, d time.Duration) {
    log.Printf("consul lock busy, retrying in %s: %v", d, e)
})

Prevention

When it happens

Trigger: Initializing a non-default Consul state workspace while another terraform process holds the lock, the Consul agent is down, or session creation/lock acquisition failed.

Common situations: Concurrent terraform runs against the same workspace; a stale lock left by a crashed run; Consul connectivity or ACL issues.

Related errors


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