juanfont/headscale · error

dst=%q: port range %q: %w

Error message

dst=%q: port range %q: %w

What it means

While unmarshalling a test destination (host:port form), the part after the colon failed parsePortRange. The error is prefixed with the full original destination and the offending port substring so the exact bad fragment is visible.

Source

Thrown at hscontrol/policy/v2/types.go:891

	switch vs := v.(type) {
	case string:
		var (
			portsPart string
			err       error
		)

		originalDst := vs

		if strings.Contains(vs, ":") {
			vs, portsPart, err = splitDestinationAndPort(vs)
			if err != nil {
				return err
			}

			ports, err := parsePortRange(portsPart)
			if err != nil {
				return fmt.Errorf(
					"dst=%q: port range %q: %w",
					originalDst, portsPart, err,
				)
			}

			ve.Ports = ports
		} else {
			return ErrHostportMissingColon
		}

		ve.Alias, err = parseAlias(vs)
		if err != nil {
			return err
		}

		if err := ve.Validate(); err != nil { //nolint:noinlineerr
			return err
		}

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Use a single port or a simple 'low-high' range: 'web:80', 'web:8080-8090'.
  2. Split comma-separated ports into separate destination entries.
  3. Keep ports within 0-65535 and range bounds ordered.

Example fix

// before
"accept": ["web:80,443"]

// after
"accept": ["web:80", "web:443"]
Defensive patterns

Strategy: validation

Validate before calling

var portRangeRe = regexp.MustCompile(`^[0-9]+(-[0-9]+)?$`)
_, ports := splitDestinationAndPortSafe(dst)
if !portRangeRe.MatchString(ports) {
    return fmt.Errorf("bad ports %q in %q; want N or N-M", ports, dst)
}

Try / catch

if err := v2.UnmarshalTestDestination(dst, &ve); err != nil {
    if strings.Contains(err.Error(), "port range") {
        // error shows dst= and the exact bad fragment; fix that entry
    }
    return err
}

Prevention

When it happens

Trigger: Destinations like 'web:80-90-100' (double range), 'web:abc', 'web:70000', or 'web:0x50'. splitDestinationAndPort succeeds but parsePortRange(portsPart) errors in the test-destination unmarshaller.

Common situations: Comma-spliced ports ('web:80,443' parsed as one fragment), ranges written with '..' instead of '-', port numbers from config copied in decimal-with-suffix form.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/a9293e541fed781c. Report an issue: GitHub.