hashicorp/nomad · error

error parsing reserved_ports: %w

Error message

error parsing reserved_ports: %w

What it means

SetNode on the network index failed to parse the node-level reserved host ports string (node.ReservedResources.Networks.ReservedHostPorts). ParsePortRanges rejected the value, so the scheduler cannot build its port-usage bitmap and the node is rejected/registration fails. The comment notes clients should have validated this, so it signals data that bypassed client-side validation.

Source

Thrown at nomad/structs/network.go:230

	// Reserved ports get merged downward. For example given an agent
	// config:
	//
	// client.reserved.reserved_ports = "22"
	// client.host_network["eth0"] = {reserved_ports = "80,443"}
	// client.host_network["eth1"] = {reserved_ports = "1-1000"}
	//
	// Addresses on taskNetworks reserve port 22
	// Addresses on eth0 reserve 22,80,443 (note 22 is also reserved!)
	// Addresses on eth1 reserve 1-1000
	globalResPorts := []uint{}

	if node.ReservedResources != nil && node.ReservedResources.Networks.ReservedHostPorts != "" {
		resPorts, err := ParsePortRanges(node.ReservedResources.Networks.ReservedHostPorts)
		if err != nil {
			// This is a fatal error that should have been
			// prevented by client validation.
			return fmt.Errorf("error parsing reserved_ports: %w", err)
		}

		globalResPorts = make([]uint, len(resPorts))
		for i, p := range resPorts {
			globalResPorts[i] = uint(p)
		}
	}

	// Filter task networks down to those with a device. For example
	// taskNetworks may contain a "bridge" interface which has no device
	// set and cannot be used to fulfill asks.
	for _, n := range taskNetworks {
		if n.Device != "" {
			idx.TaskNetworks = append(idx.TaskNetworks, n)
			idx.AvailBandwidth[n.Device] = n.MBits

			// Reserve ports
			used := idx.getUsedPortsFor(n.IP)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Fix the client's reserved_ports config to a valid comma-separated list/ranges of ports within 0-65535, ascending ranges (e.g. "22, 5900-5950")
  2. Validate the port string locally with the same rules (ParsePortRanges semantics) before registering/updating the node
  3. If the bad value is already in state, deregister and re-register the node after fixing config
  4. Upgrade clients so reserved port values are validated before being sent to servers

Example fix

// before (client config)
reserved_ports = "22, 100-50, http"
// after
reserved_ports = "22, 100-200"
Defensive patterns

Strategy: validation

Validate before calling

func validPortList(s string) bool {
	if s == "" { return true }
	for _, part := range strings.Split(s, ",") {
		part = strings.TrimSpace(part)
		lo, hi, found := strings.Cut(part, "-")
		a, err1 := strconv.Atoi(strings.TrimSpace(lo))
		b, err2 := a, error(nil)
		if found { b, err2 = strconv.Atoi(strings.TrimSpace(hi)) }
		if err1 != nil || err2 != nil || a < 0 || b > 65535 || (found && a > b) {
			return false
		}
	}
	return true
}

Try / catch

if err := client.Nodes().Update(node); err != nil && strings.Contains(err.Error(), "error parsing reserved_ports") {
	// fix node reserved_resources.networks.reserved_host_ports and retry once
}

Prevention

When it happens

Trigger: A node registers (or fingerprints update) with reserved_resources.networks.reserved_host_ports containing malformed port ranges — e.g. "foo", "70000-80000", "20-10" (reversed), or overlapping/garbage ranges — and SetNode is invoked during scheduling (AllocsFit) or tests.

Common situations: Hand-edited client config files with a bad reserved_ports list; versions/paths where client validation was skipped; state store contents written by a buggy or older client; automation writing invalid port strings into node reserved resources.

Related errors


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