hashicorp/nomad · error

error parsing port label

Error message

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

What it means

When registering an agent service, each non-empty check PortLabel is parsed with net.SplitHostPort to obtain host:port. This error wraps the SplitHostPort error (e.g. 'missing port in address' or 'too many colons in address') when the check's PortLabel is not a valid host:port pair. Unlike task services, agent checks must encode the port directly in the label, so any malformed value aborts registration.

Solutions

  1. Set the check's PortLabel to an explicit 'host:port' value, e.g. '127.0.0.1:4646', instead of a Nomad port label name
  2. Check the wrapped %v error to distinguish 'missing port' (add :port) from 'too many colons' (bracket IPv6 like [::1]:4646)
  3. Remove the port_label key entirely so the check inherits the service registration address/port
  4. Audit other checks on the same agent service for the same malformed label

Example fix

// before (agent config)
check {
  type = "http"
  port_label = "http"
}
// after
check {
  type = "http"
  port_label = "127.0.0.1:4646"
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate check port labels are host:port before applying agent config
_, _, err := net.SplitHostPort(check.PortLabel)
if err != nil {
  return fmt.Errorf("check %q port_label %q must be host:port", check.Name, check.PortLabel)
}

Type guard

func isValidHostPort(label string) bool {
  host, port, err := net.SplitHostPort(label)
  return err == nil && host != "" && port != ""
}

Prevention

When it happens

Trigger: RegisterAgentWorkload with a check whose PortLabel is a bare port label (e.g. 'http') or otherwise lacks a 'host:port' form, so net.SplitHostPort returns an error; IPv6 literals not bracketed correctly also trigger 'too many colons'.

Common situations: Setting check.port_label = "http" (a task-style port label) in agent config where a raw 'host:port' like "127.0.0.1:4646" is required; copy-pasted task service config; forgetting the port entirely.

Related errors


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

Appendix: source

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

		legacyID := service.LegacyAgentID(role)
		if legacyID != "" {
			// we intentionally swallow this error because these services likely
			// no longer exist
			_ = c.agentAPI.ServiceDeregisterOpts(legacyID, nil)
		}

		for _, check := range service.Checks {
			checkID := MakeCheckID(id, check)
			if check.Type == structs.ServiceCheckScript {
				return fmt.Errorf("service %q contains invalid check: agent checks do not support scripts", service.Name)
			}
			checkHost, checkPort := serviceReg.Address, serviceReg.Port
			if check.PortLabel != "" {
				// Unlike tasks, agents don't use port labels. Agent ports are
				// stored directly in the PortLabel.
				host, rawport, err := net.SplitHostPort(check.PortLabel)
				if err != nil {
					return fmt.Errorf("error parsing port label %q from check %q: %v", service.PortLabel, check.Name, err)
				}
				port, err := strconv.Atoi(rawport)
				if err != nil {
					return fmt.Errorf("error parsing port %q from check %q: %v", rawport, check.Name, err)
				}
				checkHost, checkPort = host, port
			}
			checkReg, err := createCheckReg(id, checkID, check, checkHost, checkPort, "")
			if err != nil {
				return fmt.Errorf("failed to add check %q: %v", check.Name, err)
			}
			ops.regChecks = append(ops.regChecks, checkReg)
		}
	}

	// Don't bother committing agent checks if we're already shutting down
	c.agentLock.Lock()
	defer c.agentLock.Unlock()

View on GitHub (pinned to 482b49bf1a)