XTLS/Xray-core · error

invalid port: {}

Error message

invalid port: {}

What it means

Thrown by PortList.UnmarshalJSON in infra/conf/common.go when the JSON value for a port list is neither a JSON string nor a JSON number. PortList accepts a comma-separated string of ports/ranges or a bare number; objects, arrays, booleans, or null fail both json.Unmarshal attempts and this error wraps the second unmarshal error.

Source

Thrown at infra/conf/common.go:249

		return json.Marshal(portStr)
	}
}

func (v PortList) String() string {
	ports := []string{}
	for _, port := range v.Range {
		ports = append(ports, port.String())
	}
	return strings.Join(ports, ",")
}

// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON
func (list *PortList) UnmarshalJSON(data []byte) error {
	var listStr string
	var number uint32
	if err := json.Unmarshal(data, &listStr); err != nil {
		if err2 := json.Unmarshal(data, &number); err2 != nil {
			return errors.New("invalid port: ", string(data)).Base(err2)
		}
	}
	rangelist := strings.Split(listStr, ",")
	for _, rangeStr := range rangelist {
		trimmed := strings.TrimSpace(rangeStr)
		if len(trimmed) > 0 {
			if strings.Contains(trimmed, "-") || strings.Contains(trimmed, "env:") {
				from, to, err := parseStringPort(trimmed)
				if err != nil {
					return errors.New("invalid port range: ", trimmed).Base(err)
				}
				list.Range = append(list.Range, PortRange{From: uint32(from), To: uint32(to)})
			} else {
				port, err := parseIntPort([]byte(trimmed))
				if err != nil {
					return errors.New("invalid port: ", trimmed).Base(err)
				}
				list.Range = append(list.Range, PortRange{From: uint32(port), To: uint32(port)})

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Use a string: "port": "80,443,1000-2000"
  2. Or a single number: "port": 443
  3. Do not use a JSON array for port lists

Example fix

// before
"port": [80, 443]

// after
"port": "80,443"
Defensive patterns

Strategy: validation

Validate before calling

func isPortListJSON(raw []byte) error {
    var s string
    if json.Unmarshal(raw, &s) == nil {
        return nil
    }
    var n uint32
    if json.Unmarshal(raw, &n) == nil {
        return nil
    }
    return errors.New("port list must be a string like \"80,443,1000-2000\" or a number")
}

Try / catch

if err := json.Unmarshal(data, &pl); err != nil {
    if strings.Contains(err.Error(), "invalid port") {
        return fmt.Errorf("routing port field %s: arrays are not supported; use a comma-separated string", data)
    }
    return err
}

Prevention

When it happens

Trigger: Fields typed conf.PortList (e.g. routing rule 'port') given "port": [80, 443] (array form is NOT supported), "port": {"from":1}, or "port": true.

Common situations: Assuming the array form works because StringList accepts arrays — PortList does not; migrating configs from tools that emit arrays of ports.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/1653e2424857c658. Report an issue: GitHub.