{"record":{"id":"7bafa103fff03f3d","repo":"hashicorp/nomad","slug":"bug-users-pool-min-must-be-0","errorCode":null,"errorMessage":"bug: users pool min must be >= 0","messagePattern":"bug: users pool min must be >= 0","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/users/dynamic/pool.go","lineNumber":82,"sourceCode":"}\n\n// disable will return true if either min or max is set to Disable (-1),\n// 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) {}","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/users/dynamic/pool.go#L64-L100","documentation":"New validates that PoolConfig.MinUGID is not negative; UGIDs are non-negative OS user/group IDs, so a negative min is a caller bug and panics. The pool maps tasks to UGID ranges and cannot represent negative IDs.","triggerScenarios":"Calling users/dynamic.New with a PoolConfig whose MinUGID < 0 (e.g. -1 used as a sentinel for 'unset').","commonSituations":"Using -1 or 0-minus values as a placeholder in config files or structs, arithmetic overflow when computing the UGID range, or misreading doNotEnable sentinel semantics.","solutions":["Set MinUGID to a non-negative value (typically >= 0 and <= MaxUGID)","Use the documented disable mechanism instead of negative values to turn the pool off","Validate user-supplied configuration (job/agent config) before constructing the PoolConfig"],"exampleFix":"// before\ncfg := &dynamic.PoolConfig{MinUGID: -1, MaxUGID: 100_000}\n// after\ncfg := &dynamic.PoolConfig{MinUGID: 100_000, MaxUGID: 200_000}","handlingStrategy":"validation","validationCode":"if cfg != nil && cfg.MinUGID < 0 { return fmt.Errorf(\"MinUGID must be >= 0, got %d\", cfg.MinUGID) }","typeGuard":null,"tryCatchPattern":"defer func() { if r := recover(); r != nil { if s, ok := r.(string); ok && strings.Contains(s, \"users pool min must be >= 0\") { log.Fatalf(\"invalid MinUGID: %v\", s) }; panic(r) } }()","preventionTips":["Clamp or reject negative UGID values when loading config","Do not use -1 as an unset sentinel; use a separate bool or the disable mechanism","Unit-test pool construction with boundary values"],"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"}