micro/go-micro · error

unable to extract port range

Error message

unable to extract port range

What it means

This error is thrown by util/net.Listen when parsing a user-supplied port range string like "8000-9000". The range is split on the separator and each half must parse as an integer via strconv.Atoi; the first half (the minimum port) failed to parse. It is a guard so the pool can know which ports to try binding.

Source

Thrown at internal/util/net/net.go:54

	host, ports, err := net.SplitHostPort(addr)
	if err != nil {
		return nil, err
	}

	// try to extract port range
	prange := strings.Split(ports, "-")

	// single port
	if len(prange) < 2 {
		return fn(addr)
	}

	// we have a port range

	// extract min port
	min, err := strconv.Atoi(prange[0])
	if err != nil {
		return nil, errors.New("unable to extract port range")
	}

	// extract max port
	max, err := strconv.Atoi(prange[1])
	if err != nil {
		return nil, errors.New("unable to extract port range")
	}

	// range the ports
	for port := min; port <= max; port++ {
		// try bind to host:port
		ln, err := fn(HostPort(host, port))
		if err == nil {
			return ln, nil
		}

		// hit max port
		if port == max {

View on GitHub (pinned to 24529f1404)

Solutions

  1. Check the port-range value actually passed to Listen and print it before parsing; fix the min portion so it is a plain integer (e.g. "8000-9000").
  2. Trim whitespace and remove stray characters before the range reaches Listen.
  3. If the value comes from env/config, validate it with a regex like ^[0-9]+-[0-9]+$ at startup and fail fast with a clear message.
  4. Use named constants or ints in config instead of a raw string to avoid formatting errors.

Example fix

// before
addr, err := net.Listen(pool, os.Getenv("PORT_RANGE")) // PORT_RANGE="8 - 9000"
// after
pr := strings.ReplaceAll(os.Getenv("PORT_RANGE"), " ", "") // "8000-9000"
if !regexp.MustCompile(`^[0-9]+-[0-9]+$`).MatchString(pr) {
    return fmt.Errorf("invalid PORT_RANGE: %q", os.Getenv("PORT_RANGE"))
}
addr, err := net.Listen(pool, pr)
Defensive patterns

Strategy: validation

Validate before calling

func validPortRange(s string) bool {
    parts := strings.Split(s, "-")
    if len(parts) != 2 { return false }
    min, err1 := strconv.Atoi(strings.TrimSpace(parts[0]))
    max, err2 := strconv.Atoi(strings.TrimSpace(parts[1]))
    return err1 == nil && err2 == nil && min <= max && min > 0 && max <= 65535
}

Type guard

func isPortRange(s string) (min, max int, ok bool) {
    parts := strings.Split(s, "-")
    if len(parts) != 2 { return 0, 0, false }
    var e1, e2 error
    min, e1 = strconv.Atoi(strings.TrimSpace(parts[0]))
    max, e2 = strconv.Atoi(strings.TrimSpace(parts[1]))
    return min, max, e1 == nil && e2 == nil
}

Try / catch

addr, err := net.Listen(pool, prange)
if err != nil {
    if strings.Contains(err.Error(), "unable to extract port range") {
        return fmt.Errorf("malformed PORT_RANGE %q: want e.g. 8000-9000", prange)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Listen (directly or via a pool that resolves free ports) with a port range whose min portion is not a valid integer, e.g. MIN_PORT unset so the range string is "-9000", or "8000 -9000" with stray spaces, or a reversed/garbled range like "x-9000".

Common situations: Misconfigured environment variables or config files for port ranges (MIN_PORT/MAX_PORT); YAML/JSON values quoted or containing whitespace; empty string producing a split with a non-numeric element; platform-specific startup scripts exporting malformed values.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/787c4fb178c9b975. Report an issue: GitHub.