{"record":{"id":"2f565a0e41f628c2","repo":"hashicorp/nomad","slug":"bug-users-pool-max-must-be-min","errorCode":null,"errorMessage":"bug: users pool max must be >= min","messagePattern":"bug: users pool max must be >= min","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/users/dynamic/pool.go","lineNumber":85,"sourceCode":"// indicating the client should not enable the dynamic workload users\n// functionality\nfunc (p *PoolConfig) disable() bool {\n\treturn p.MinUGID == doNotEnable || p.MaxUGID == doNotEnable\n}\n\n// New creates a Pool with the given PoolConfig options.\nfunc New(opts *PoolConfig) Pool {\n\tif opts == nil {\n\t\tpanic(\"bug: users pool cannot be nil\")\n\t}\n\tif opts.disable() {\n\t\treturn new(noopPool)\n\t}\n\tif opts.MinUGID < 0 {\n\t\tpanic(\"bug: users pool min must be >= 0\")\n\t}\n\tif opts.MaxUGID < opts.MinUGID {\n\t\tpanic(\"bug: users pool max must be >= min\")\n\t}\n\t// a small but reasonable number of tasks to expect\n\tconst defaultPoolCapacity = 32\n\treturn &pool{\n\t\tmin:  UGID(opts.MinUGID),\n\t\tmax:  UGID(opts.MaxUGID),\n\t\tlock: new(sync.Mutex),\n\t\tused: set.New[UGID](defaultPoolCapacity),\n\t}\n}\n\n// noopPool is an implementation of Pool that does not allow acquiring ugids\ntype noopPool struct{}\n\nfunc (*noopPool) Restore(UGID) {}\nfunc (*noopPool) Acquire() (UGID, error) {\n\treturn 0, errors.New(\"dynamic workload users disabled\")\n}","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/users/dynamic/pool.go#L67-L103","documentation":"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.","triggerScenarios":"Calling users/dynamic.New with MaxUGID less than MinUGID, e.g. swapped values or a range that collapses below min.","commonSituations":"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.","solutions":["Ensure MaxUGID >= MinUGID in the PoolConfig before calling New","Validate config input (or clamp/swaps) when loading from user-facing configuration","Add a unit test around pool construction to catch inverted ranges early"],"exampleFix":"// before\ncfg := &dynamic.PoolConfig{MinUGID: 200_000, MaxUGID: 100_000} // swapped\n// after\ncfg := &dynamic.PoolConfig{MinUGID: 100_000, MaxUGID: 200_000}","handlingStrategy":"validation","validationCode":"if cfg != nil && cfg.MaxUGID < cfg.MinUGID { return fmt.Errorf(\"MaxUGID (%d) must be >= MinUGID (%d)\", cfg.MaxUGID, cfg.MinUGID) }","typeGuard":null,"tryCatchPattern":"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) } }()","preventionTips":["Validate the range ordering before constructing PoolConfig","Keep range values in named constants to avoid field-order mistakes","Add a constructor wrapper in your codebase that normalizes min/max ordering"],"tags":["panic","configuration","validation"],"backgroundTag":"invalid-config-range","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}