hashicorp/nomad · error

Lock delay and TTL must be positive

Error message

Lock delay and TTL must be positive

What it means

VariableLock.Validate rejects a lock whose LockDelay or TTL is negative. Lock delay and time-to-live are durations and must be zero or positive; a negative value is treated as a configuration error and errNegativeDelayOrTTL is appended to the multierror. Nomad additionally enforces a positive-only TTL range via errInvalidTTL.

Source

Thrown at nomad/structs/variables.go:76

	maxVariableLockTTL = 24 * time.Hour

	// defaultLockTTL is the default value used to maintain a lock before it needs to
	// be renewed. The actual value comes from the experience with Consul.
	defaultLockTTL = 15 * time.Second

	// defaultLockDelay is the default a lock will be blocked after the TTL
	// went by without any renews. It is intended to prevent split brain situations.
	// The actual value comes from the experience with Consul.
	defaultLockDelay = 15 * time.Second
)

var (
	errNoPath             = errors.New("missing path")
	errNoNamespace        = errors.New("missing namespace")
	errNoLock             = errors.New("missing lock ID")
	errWildCardNamespace  = errors.New("can not target wildcard (\"*\")namespace")
	errQuotaExhausted     = errors.New("variables are limited to 64KiB in total size")
	errNegativeDelayOrTTL = errors.New("Lock delay and TTL must be positive")
	errInvalidTTL         = errors.New("TTL must be between 10 seconds and 24 hours")
)

// VariableMetadata is the metadata envelope for a Variable, it is the list
// object and is shared data between an VariableEncrypted and a
// VariableDecrypted object.
type VariableMetadata struct {
	Namespace string
	Path      string

	// Lock represents a variable which is used for locking functionality.
	Lock *VariableLock `json:",omitempty"`

	CreateIndex uint64
	CreateTime  int64
	ModifyIndex uint64
	ModifyTime  int64
}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set LockDelay and TTL to non-negative durations (e.g. 5 * time.Second).
  2. Clamp or validate user-supplied durations at config parse time before constructing VariableLock.
  3. Remember TTL must additionally fall between minVariableLockTTL (10s) and maxVariableLockTTL (24h) to pass Validate.

Example fix

// before
lock := &structs.VariableLock{LockDelay: -5 * time.Second}
err := lock.Validate() // Lock delay and TTL must be positive
// after
lock := &structs.VariableLock{LockDelay: 5 * time.Second, TTL: 60 * time.Second}
err := lock.Validate()
Defensive patterns

Strategy: validation

Validate before calling

func lockDurationsValid(lockDelay, ttl time.Duration) bool {
	return lockDelay >= 0 && ttl >= 0
}

Try / catch

if err := lock.Validate(); err != nil {
	if strings.Contains(err.Error(), "must be positive") {
		return fmt.Errorf("LockDelay/TTL must be >= 0, got LockDelay=%s TTL=%s", lock.LockDelay, lock.TTL)
	}
	return err
}

Prevention

When it happens

Trigger: Defining a VariableLock with LockDelay or TTL set to a negative time.Duration (e.g. -5 * time.Second) and calling Validate; also reproduced in TestStructs_Lock_Validate with LockDelay: -5 * time.Second.

Common situations: Hand-writing HCL/JSON lock config with a mistaken minus sign; templating durations from config where a negative default leaks in; unit tests intentionally checking invalid configs.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/f8727805a6dc01cd. Report an issue: GitHub.