hashicorp/terraform · error

consul lock was lost

Error message

consul lock was lost

What it means

NestingGroup blocks are unconditionally a single, always-present group; the MinItems/MaxItems limits do not apply and must both be left at 0. Setting either is a schema definition error.

Solutions

  1. Set both MinItems: 0 and MaxItems: 0 on the NestingGroup block.
  2. If you need occurrence limits, use NestingList or NestingSet instead of NestingGroup.

Example fix

// before
"features": { Nesting: configschema.NestingGroup, MinItems: 1, MaxItems: 1 },
// after
"features": { Nesting: configschema.NestingGroup, MinItems: 0, MaxItems: 0 },
Defensive patterns

Strategy: validation

Validate before calling

// NestingGroup must keep MinItems and MaxItems at 0.
func validGroupCounts(nb *configschema.NestedBlock) bool {
    return nb.Nesting != configschema.NestingGroup || (nb.MinItems == 0 && nb.MaxItems == 0)
}

Type guard

func groupCountsZero(min, max int) bool { return min == 0 && max == 0 }

Prevention

When it happens

Trigger: A NestedBlock with Nesting: NestingGroup and MinItems or MaxItems != 0. Guard at internal_validate.go:82 is `blockS.MinItems != 0 || blockS.MaxItems != 0`.

Common situations: Changing NestingList to NestingGroup without clearing the count fields; assuming group semantics still need a min count.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/19a470b199708ded. Report an issue: GitHub.

Appendix: source

Thrown at internal/backend/remote-state/consul/client.go:40

	"github.com/hashicorp/terraform/internal/states/statemgr"
	"github.com/hashicorp/terraform/internal/tfdiags"
)

const (
	lockSuffix     = "/.lock"
	lockInfoSuffix = "/.lockinfo"

	// The Session TTL associated with this lock.
	lockSessionTTL = "15s"

	// the delay time from when a session is lost to when the
	// lock is released by the server
	lockDelay = 5 * time.Second
	// interval between attempts to reacquire a lost lock
	lockReacquireInterval = 2 * time.Second
)

var lostLockErr = errors.New("consul lock was lost")

// RemoteClient is a remote client that stores data in Consul.
type RemoteClient struct {
	Client *consulapi.Client
	Path   string
	GZip   bool

	mu sync.Mutex
	// lockState is true if we're using locks
	lockState bool

	// The index of the last state we wrote.
	// If this is > 0, Put will perform a CAS to ensure that the state wasn't
	// changed during the operation. This is important even with locks, because
	// if the client loses the lock for some reason, then reacquires it, we
	// need to make sure that the state was not modified.
	modifyIndex uint64

View on GitHub (pinned to d32a084675)