fatedier/frp · error

first and second range numbers are not in pairs

Error message

first and second range numbers are not in pairs

What it means

Thrown by parseNumberRangePair in pkg/config/template.go when building a proxy's port-pair mapping (e.g. TCP port multiplexing / per-port plugin configs that pair two port ranges). Both range strings are expanded into concrete number lists via util.ParseRangeNumbers, and the two lists must contain exactly the same count so element i of the first range maps to element i of the second. If the counts differ, no 1:1 pairing exists and the function aborts with this error. It surfaces during TOML/INI/JSON config conversion into the v1 format, before the client connects.

Source

Thrown at pkg/config/template.go:38

	"github.com/fatedier/frp/pkg/util/util"
)

type NumberPair struct {
	First  int64
	Second int64
}

func parseNumberRangePair(firstRangeStr, secondRangeStr string) ([]NumberPair, error) {
	firstRangeNumbers, err := util.ParseRangeNumbers(firstRangeStr)
	if err != nil {
		return nil, err
	}
	secondRangeNumbers, err := util.ParseRangeNumbers(secondRangeStr)
	if err != nil {
		return nil, err
	}
	if len(firstRangeNumbers) != len(secondRangeNumbers) {
		return nil, fmt.Errorf("first and second range numbers are not in pairs")
	}
	pairs := make([]NumberPair, 0, len(firstRangeNumbers))
	for i := range firstRangeNumbers {
		pairs = append(pairs, NumberPair{
			First:  firstRangeNumbers[i],
			Second: secondRangeNumbers[i],
		})
	}
	return pairs, nil
}

func parseNumberRange(firstRangeStr string) ([]int64, error) {
	return util.ParseRangeNumbers(firstRangeStr)
}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Make both ranges expand to the same number of ports, e.g. "2000-2003" with "6000-6003" (4 ports each), or pair single values "2000" with "6000".
  2. Count both sides explicitly: N numbers on the first side requires exactly N numbers on the second side (a range a-b expands to b-a+1 numbers).
  3. If you intended one value to map to many, restructure the config as multiple proxy entries or use a range on both sides of equal length.

Example fix

# before
range1 = "2000-2003"
range2 = "6000-6001"

# after
range1 = "2000-2003"
range2 = "6000-6003"
Defensive patterns

Strategy: validation

Validate before calling

// Expand both ranges and require equal lengths before calling parseNumberRangePair.
func rangesPairable(a, b string) error {
	na, err := util.ParseRangeNumbers(a)
	if err != nil {
		return fmt.Errorf("first range invalid: %w", err)
	}
	nb, err := util.ParseRangeNumbers(b)
	if err != nil {
		return fmt.Errorf("second range invalid: %w", err)
	}
	if len(na) != len(nb) {
		return fmt.Errorf("range %q has %d numbers but %q has %d; counts must match", a, len(na), b, len(nb))
	}
	return nil
}

Type guard

func isPairableRange(a, b string) bool {
	na, err1 := util.ParseRangeNumbers(a)
	nb, err2 := util.ParseRangeNumbers(b)
	return err1 == nil && err2 == nil && len(na) == len(nb)
}

Prevention

When it happens

Trigger: A proxy config declares two ranges that expand to different lengths, e.g. first range "2000-2003" (4 numbers) paired with second range "6000-6001" (2 numbers), or a single value "8080" paired with a range "9000-9002". Any parseNumberRangePair call where len(ParseRangeNumbers(a)) != len(ParseRangeNumbers(b)).

Common situations: Converting an old frp INI/TOML config (with paired port-range settings such as plugin port mappings) where one side of the pair was edited and the other was not; copy-paste of range examples from docs where the sample ranges have different spans; assuming a single value can pair with a multi-value range.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/70447390f81c9ff0. Report an issue: GitHub.