hashicorp/nomad · error
stochastic dynamic port selection failed
Error message
stochastic dynamic port selection failed
What it means
getDynamicPortsStochastic picks dynamic ports by repeatedly drawing random values in [minDynamicPort, maxDynamicPort) and checking collisions. After maxRandPortAttempts failed draws for a single port, it gives up with "stochastic dynamic port selection failed". This is the fallback/randomized path used when precise enumeration isn't possible, and it fails when the range is too full for random search to find free ports.
Source
Thrown at nomad/structs/network.go:716
// getDynamicPortsStochastic takes the nodes used port bitmap which may be nil
// if no ports have been allocated yet, any ports already offered in the caller,
// and the network ask. It returns a set of unused ports to fulfil the ask's
// DynamicPorts or an error if it failed. An error does not mean the ask can not
// be satisfied as the method has a fixed amount of random probes and if these
// fail, the search is aborted.
func getDynamicPortsStochastic(nodeUsed Bitmap, portsInOffer []int, minDynamicPort, maxDynamicPort int, reservedPorts []Port, count int) ([]int, error) {
var reserved, dynamic []int
for _, port := range reservedPorts {
reserved = append(reserved, port.Value)
}
for range count {
attempts := 0
PICK:
attempts++
if attempts > maxRandPortAttempts {
return nil, fmt.Errorf("stochastic dynamic port selection failed")
}
randPort := minDynamicPort
if maxDynamicPort-minDynamicPort > 0 {
randPort = randPort + rand.Intn(maxDynamicPort-minDynamicPort)
}
if nodeUsed != nil && nodeUsed.Check(uint(randPort)) {
goto PICK
}
for _, ports := range [][]int{reserved, dynamic} {
if isPortReserved(ports, randPort) {
goto PICK
}
}
// the pick conflicted with a previous pick that hasn't been saved to
// the index yetView on GitHub (pinned to 482b49bf1a)
Solutions
- Increase the dynamic port range size on the client.
- Reduce concurrent dynamic port demand (fewer dynamic ports per allocation).
- Retry scheduling on another node with free port capacity.
- Raise maxRandPortAttempts if ranges are legitimately dense but not exhausted (library-level tuning).
Example fix
// before # range too small for load dynamic_port_range = "20000-20050" // after dynamic_port_range = "20000-40000"
Defensive patterns
Strategy: fallback
Validate before calling
utilization := float64(usedDynamicPorts(node)) / float64(rangeSize)
if utilization > 0.9 {
return fmt.Errorf("dynamic port range %.0f%% full on %s; pick another node", utilization*100, node.ID)
} Type guard
func rangeHealthy(used, size, attempts int) bool { return size-used > attempts } Try / catch
null
Prevention
- Ensure the dynamic range is orders of magnitude larger than per-node port demand
- Retry scheduling with node re-selection when port selection fails
- Keep dynamic_port_range within ephemeral (ip_local_port_range) limits to avoid OS-level clashes
When it happens
Trigger: AssignPorts' dynamic port ask runs maxRandPortAttempts random draws without finding a free port: the dynamic range is nearly saturated on that address (or range is tiny relative to requested count).
Common situations: Heavily loaded nodes with thousands of allocations; very narrow dynamic_port_range configs; tests with artificially small ranges causing >maxRandPortAttempts collisions.
Related errors
- dynamic port selection failed
- bitmap must be positive size
- can't specify empty port
- port must be > 0
- port must be < %d but found %d
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/cced14bcec7ed713.
Report an issue: GitHub.