fatedier/frp · error
range number is invalid
Error message
range number is invalid
What it means
Thrown while unmarshalling a PortsRange when a two-part range has maxNum < minNum, i.e. the range is inverted. Both bounds parsed successfully as integers, but PortsRange semantics require Start <= End, so an inverted range is rejected with this plain (non-wrapped) message from PortsRange.UnmarshalJSON.
Source
Thrown at pkg/config/types/types.go:165
case 1:
// single number
singleNum, err := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
if err != nil {
return nil, fmt.Errorf("range number is invalid, %v", err)
}
out = append(out, PortsRange{Single: int(singleNum)})
case 2:
// range numbers
minNum, err := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
if err != nil {
return nil, fmt.Errorf("range number is invalid, %v", err)
}
maxNum, err := strconv.ParseInt(strings.TrimSpace(numArray[1]), 10, 64)
if err != nil {
return nil, fmt.Errorf("range number is invalid, %v", err)
}
if maxNum < minNum {
return nil, fmt.Errorf("range number is invalid")
}
out = append(out, PortsRange{Start: int(minNum), End: int(maxNum)})
default:
return nil, fmt.Errorf("range number is invalid")
}
}
return out, nil
}
View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Swap the bounds so the smaller number comes first: "2000-9000".
- If the config is generated, fix the template so min is emitted before max.
- If a reversed range was intentional (it is not supported), split into explicit individual ports or reorder at the source.
Example fix
# before remotePort = "9000-2000" # after remotePort = "2000-9000"
Defensive patterns
Strategy: validation
Validate before calling
// Ensure every range token satisfies min <= max.
func rangesOrdered(s string) error {
for _, tok := range strings.Split(s, ",") {
parts := strings.Split(tok, "-")
if len(parts) != 2 {
continue
}
min, err1 := strconv.ParseInt(strings.TrimSpace(parts[0]), 10, 64)
max, err2 := strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 64)
if err1 != nil || err2 != nil {
continue
}
if max < min {
return fmt.Errorf("inverted range %q: max %d < min %d", tok, max, min)
}
}
return nil
} Type guard
func isOrderedRange(tok string) bool {
parts := strings.Split(tok, "-")
if len(parts) != 2 {
return true
}
min, e1 := strconv.ParseInt(strings.TrimSpace(parts[0]), 10, 64)
max, e2 := strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 64)
return e1 == nil && e2 == nil && min <= max
} Prevention
- Always write ranges smallest-to-largest.
- In generated configs, emit min(maxvar, minvar) and max(...) to guard swapped variables.
- Add a lint rule rejecting inverted ranges.
When it happens
Trigger: PortsRange unmarshal on any token 'X-Y' where Y < X, e.g. "9000-2000". Also from auto-generated configs where min/max variables were swapped during templating.
Common situations: Hand-written configs with the bounds reversed; generated configs where $MIN and $MAX env vars are interchanged; refactoring that renamed bounds and swapped their order.
Related errors
- first and second range numbers are not in pairs
- range number is invalid, %v
- exec configuration is required when type is 'exec'
- file path cannot be empty
- exec command cannot be empty
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/c1833ec4eefcb6d8.
Report an issue: GitHub.