hashicorp/nomad · error

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

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.

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)

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.