hashicorp/nomad · error

bug: users pool cannot be nil

Error message

bug: users pool cannot be nil

What it means

users/dynamic.New(opts) requires a non-nil *PoolConfig; passing nil is treated as a caller bug and panics with this message. A nil config provides no UGID range, so the pool cannot be constructed safely. The 'bug:' prefix indicates it should only happen from incorrect library usage, not environmental conditions.

Source

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

type PoolConfig struct {
	// MinUGID is the minimum value for a UGID allocated from the pool.
	MinUGID int

	// MaxUGID is the maximum value for a UGID allocated from the pool.
	MaxUGID int
}

// disable will return true if either min or max is set to Disable (-1),
// 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),
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Pass a valid &users dynamic.PoolConfig{} with MinUGID and MaxUGID set before calling New
  2. Add a nil-check in your own config-loading path and fall back to sane defaults
  3. If the pool should be disabled, use PoolConfig fields that make disable() true rather than passing nil

Example fix

// before
var cfg *dynamic.PoolConfig
pool := dynamic.New(cfg)
// after
cfg := &dynamic.PoolConfig{MinUGID: 100_000, MaxUGID: 200_000}
pool := dynamic.New(cfg)
Defensive patterns

Strategy: validation

Validate before calling

func validPoolConfig(cfg *dynamic.PoolConfig) bool { return cfg != nil && cfg.MinUGID >= 0 && cfg.MaxUGID >= cfg.MinUGID }

Type guard

func cfgNotNil(cfg *dynamic.PoolConfig) bool { return cfg != nil }

Try / catch

defer func() { if r := recover(); r != nil { if s, ok := r.(string); ok && strings.Contains(s, "users pool cannot be nil") { log.Fatalf("nil PoolConfig passed to dynamic.New") }; panic(r) } }()

Prevention

When it happens

Trigger: Calling users/dynamic.New(nil) directly, or via pool.NewClient / helpers that pass an uninitialized *PoolConfig pointer.

Common situations: Declaring a *PoolConfig variable without initializing it, conditional config loading that leaves the pointer nil, or wiring errors in client setup code.

Related errors


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