hashicorp/nomad · error

error parsing port from check

Error message

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

What it means

After splitting the check's PortLabel, the raw port string is converted with strconv.Atoi. When the host portion after the colon is not a numeric port (or is out of the valid range), Atoi fails and registration of the agent service is aborted with this message, wrapping the conversion error.

Solutions

  1. Replace the non-numeric port with the actual numeric port in the check PortLabel (e.g. '127.0.0.1:4646')
  2. Check the wrapped Atoi error text for the offending value and fix typos
  3. If the port comes from a template, ensure the value is interpolated to a plain integer before the agent config loads
  4. Prefer omitting port_label so the check uses the service's registered port

Example fix

// before
port_label = "127.0.0.1:http"
// after
port_label = "127.0.0.1:4646"
Defensive patterns

Strategy: validation

Validate before calling

if _, port, err := net.SplitHostPort(check.PortLabel); err != nil {
  // split failed
} else if _, convErr := strconv.Atoi(port); convErr != nil {
  return fmt.Errorf("check %q port %q is not numeric", check.Name, port)
}

Type guard

func isNumericPort(label string) bool {
  _, port, err := net.SplitHostPort(label)
  if err != nil { return false }
  n, err := strconv.Atoi(port)
  return err == nil && n > 0 && n <= 65535
}

Prevention

When it happens

Trigger: A check PortLabel like '127.0.0.1:http' or '127.0.0.1:4646x' — SplitHostPort succeeds but the port component is non-numeric, so strconv.Atoi(rawport) errors and the wrapped message is returned.

Common situations: Using a service/port name instead of a number in an agent check port label; typo in the port; templating that left a placeholder like '${port}' unexpanded.

Related errors


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

Appendix: source

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

			_ = 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()
	select {
	case <-c.shutdownCh:
		return nil
	default:

View on GitHub (pinned to 482b49bf1a)