hashicorp/nomad · error

error parsing address %q: not an IP address

Error message

error parsing address %q: not an IP address

What it means

When SplitHostPort reports 'missing port in address', parseAddress treats the whole input as the host — but only if net.ParseIP can parse it. A non-IP string with no port (e.g. a bare hostname like 'consul.service.dc1') is rejected because Consul service addresses here must be IP literals.

Source

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

		return nil
	}

	sidecarID := id + sidecarSuffix
	return services[sidecarID]
}

func parseAddress(raw string, port int) (api.ServiceAddress, error) {
	result := api.ServiceAddress{}
	addr, portStr, err := net.SplitHostPort(raw)
	// Error message from Go's net/ipsock.go
	if err != nil {
		if !strings.Contains(err.Error(), "missing port in address") {
			return result, fmt.Errorf("error parsing address %q: %v", raw, err)
		}

		// Use the whole input as the address if there wasn't a port.
		if ip := net.ParseIP(raw); ip == nil {
			return result, fmt.Errorf("error parsing address %q: not an IP address", raw)
		}
		addr = raw
	}

	if portStr != "" {
		port, err = strconv.Atoi(portStr)
		if err != nil {
			return result, fmt.Errorf("error parsing port %q: %v", portStr, err)
		}
	}

	result.Address = addr
	result.Port = port
	return result, nil
}

// morph the tagged_addresses map into the structure consul api wants
func parseTaggedAddresses(m map[string]string, port int) (map[string]api.ServiceAddress, error) {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Use an IP literal for the address field, or include a port so SplitHostPort succeeds and the IP check path is bypassed appropriately.
  2. Resolve the hostname to an IP before configuring (or let address_mode="host" use the interface address).
  3. If a hostname is intended, use the correct Nomad field that accepts hostnames instead of this IP-only parser path.

Example fix

// before
service { address = "web.service.consul" }
// after
service { address = "10.0.4.12", port = 8080 }
Defensive patterns

Strategy: validation

Validate before calling

func ensureIPOrIPPort(raw string) error {
  host, port, err := net.SplitHostPort(raw)
  if err != nil {
    if net.ParseIP(raw) == nil {
      return fmt.Errorf("%q is neither an IP nor host:port", raw)
    }
    return nil
  }
  _ = host
  if _, err := strconv.Atoi(port); err != nil {
    return fmt.Errorf("port %q in %q is not numeric", port, raw)
  }
  return nil
}

Type guard

func isIPLiteral(s string) bool { return net.ParseIP(s) != nil }

Try / catch

sa, err := parseAddress(raw, port)
if err != nil {
  if strings.Contains(err.Error(), "not an IP address") {
    ip, lookupErr := net.LookupHost(raw)
    if lookupErr == nil { raw = ip[0] }
  }
}

Prevention

When it happens

Trigger: Calling the service registration path with an address string that is a DNS hostname or arbitrary text with no port, where an IP:port or IP is expected (e.g. service address fields fed from templating).

Common situations: Using consul service DNS names instead of IPs in address fields; template variables that resolve to hostnames; typos leaving the address empty of a port and not an IP.

Related errors


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