hashicorp/nomad · error

bug: users pool max must be >= min

Error message

bug: users pool max must be >= min

What it means

New enforces that PoolConfig.MaxUGID >= MinUGID; an inverted or empty-negative range is a caller bug and panics. The pool draws random UGIDs from [MinUGID, MaxUGID], which must be a valid ordered range.

Source

Thrown at helper/users/dynamic/pool.go:85

// indicating the client should not enable the dynamic workload users
// functionality
func (p *PoolConfig) disable() bool {
	return p.MinUGID == doNotEnable || p.MaxUGID == doNotEnable
}

// New creates a Pool with the given PoolConfig options.
func New(opts *PoolConfig) Pool {
	if opts == nil {
		panic("bug: users pool cannot be nil")
	}
	if opts.disable() {
		return new(noopPool)
	}
	if opts.MinUGID < 0 {
		panic("bug: users pool min must be >= 0")
	}
	if opts.MaxUGID < opts.MinUGID {
		panic("bug: users pool max must be >= min")
	}
	// a small but reasonable number of tasks to expect
	const defaultPoolCapacity = 32
	return &pool{
		min:  UGID(opts.MinUGID),
		max:  UGID(opts.MaxUGID),
		lock: new(sync.Mutex),
		used: set.New[UGID](defaultPoolCapacity),
	}
}

// noopPool is an implementation of Pool that does not allow acquiring ugids
type noopPool struct{}

func (*noopPool) Restore(UGID) {}
func (*noopPool) Acquire() (UGID, error) {
	return 0, errors.New("dynamic workload users disabled")
}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure MaxUGID >= MinUGID in the PoolConfig before calling New
  2. Validate config input (or clamp/swaps) when loading from user-facing configuration
  3. Add a unit test around pool construction to catch inverted ranges early

Example fix

// before
cfg := &dynamic.PoolConfig{MinUGID: 200_000, MaxUGID: 100_000} // swapped
// after
cfg := &dynamic.PoolConfig{MinUGID: 100_000, MaxUGID: 200_000}
Defensive patterns

Strategy: validation

Validate before calling

if cfg != nil && cfg.MaxUGID < cfg.MinUGID { return fmt.Errorf("MaxUGID (%d) must be >= MinUGID (%d)", cfg.MaxUGID, cfg.MinUGID) }

Try / catch

defer func() { if r := recover(); r != nil { if s, ok := r.(string); ok && strings.Contains(s, "users pool max must be >= min") { log.Fatalf("inverted UGID range: %v", s) }; panic(r) } }()

Prevention

When it happens

Trigger: Calling users/dynamic.New with MaxUGID less than MinUGID, e.g. swapped values or a range that collapses below min.

Common situations: Swapped order of struct fields when constructing PoolConfig, config where an operator set a smaller max than min, or computed ranges where MaxUGID was adjusted downward below min.

Related errors


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