hashicorp/nomad · error

error parsing port label %q from service %q: %v

Error message

error parsing port label %q from service %q: %v

What it means

When registering group-level (agent) services, Nomad expects service.PortLabel to contain 'host:port' (agents do not use port labels directly). net.SplitHostPort failing on the label produces this error, embedding the original parse failure.

Source

Thrown at command/agent/consul/service_client.go:1225

// Script checks are not supported and will return an error. Registration is
// asynchronous.
//
// Agents will be deregistered when Shutdown is called.
//
// Note: no need to manually plumb Consul namespace into the agent service registration
// or its check registrations, because the Nomad Client's Consul Client will already
// have the Nomad Client's Consul Namespace set on startup.
func (c *ServiceClient) RegisterAgent(role string, services []*structs.Service) error {
	ops := operations{}

	for _, service := range services {
		id := makeAgentServiceID(role, service)

		// Unlike tasks, agents don't use port labels. Agent ports are
		// stored directly in the PortLabel.
		host, rawport, err := net.SplitHostPort(service.PortLabel)
		if err != nil {
			return fmt.Errorf("error parsing port label %q from service %q: %v", service.PortLabel, service.Name, err)
		}
		port, err := strconv.Atoi(rawport)
		if err != nil {
			return fmt.Errorf("error parsing port %q from service %q: %v", rawport, service.Name, err)
		}
		serviceReg := &api.AgentServiceRegistration{
			ID:      id,
			Name:    service.Name,
			Tags:    service.Tags,
			Address: host,
			Port:    port,
			// This enables the consul UI to show that Nomad registered this service
			Meta: map[string]string{
				"external-source": "nomad",
			},
		}
		ops.regServices = append(ops.regServices, serviceReg)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set the service's address/port via address_mode or use the interpolated form 'host:port' (e.g. ${NOMAD_IP_http}:${NOMAD_PORT_http}) in PortLabel.
  2. Ensure the interpolation resolves — the referenced port must exist so variables are not empty.
  3. For IPv6, bracket the host part ([fd00::1]:8080) so SplitHostPort succeeds.

Example fix

// before
service {
  name = "api"
  port = "http" // bare label for group service
}
// after
service {
  name = "api"
  port     = "http"
  provider = "consul"
  address_mode = "host" // or use ${NOMAD_IP_http}:${NOMAD_PORT_http}
}
Defensive patterns

Strategy: validation

Validate before calling

if _, _, err := net.SplitHostPort(service.PortLabel); err != nil {
  return fmt.Errorf("service %q: PortLabel %q must be host:port", service.Name, service.PortLabel)
}

Type guard

func isHostPort(label string) bool {
  _, _, err := net.SplitHostPort(label)
  return err == nil
}

Prevention

When it happens

Trigger: Registering a service whose PortLabel is not of the form address:port — e.g. a bare port label like 'http' instead of '${NOMAD_IP_http}:${NOMAD_PORT_http}', or an IPv6 literal without brackets.

Common situations: Bridge/network-mode jobs using a plain port label for a group service; interpolation producing an empty string; IPv6 addresses passed unbracketed into SplitHostPort.

Related errors


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