{"record":{"id":"1653e2424857c658","repo":"XTLS/Xray-core","slug":"invalid-port","errorCode":null,"errorMessage":"invalid port: {}","messagePattern":"invalid port: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"infra/conf/common.go","lineNumber":249,"sourceCode":"\t\treturn json.Marshal(portStr)\n\t}\n}\n\nfunc (v PortList) String() string {\n\tports := []string{}\n\tfor _, port := range v.Range {\n\t\tports = append(ports, port.String())\n\t}\n\treturn strings.Join(ports, \",\")\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON\nfunc (list *PortList) UnmarshalJSON(data []byte) error {\n\tvar listStr string\n\tvar number uint32\n\tif err := json.Unmarshal(data, &listStr); err != nil {\n\t\tif err2 := json.Unmarshal(data, &number); err2 != nil {\n\t\t\treturn errors.New(\"invalid port: \", string(data)).Base(err2)\n\t\t}\n\t}\n\trangelist := strings.Split(listStr, \",\")\n\tfor _, rangeStr := range rangelist {\n\t\ttrimmed := strings.TrimSpace(rangeStr)\n\t\tif len(trimmed) > 0 {\n\t\t\tif strings.Contains(trimmed, \"-\") || strings.Contains(trimmed, \"env:\") {\n\t\t\t\tfrom, to, err := parseStringPort(trimmed)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.New(\"invalid port range: \", trimmed).Base(err)\n\t\t\t\t}\n\t\t\t\tlist.Range = append(list.Range, PortRange{From: uint32(from), To: uint32(to)})\n\t\t\t} else {\n\t\t\t\tport, err := parseIntPort([]byte(trimmed))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.New(\"invalid port: \", trimmed).Base(err)\n\t\t\t\t}\n\t\t\t\tlist.Range = append(list.Range, PortRange{From: uint32(port), To: uint32(port)})","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/infra/conf/common.go#L231-L267","documentation":"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.","triggerScenarios":"Fields typed conf.PortList (e.g. routing rule 'port') given \"port\": [80, 443] (array form is NOT supported), \"port\": {\"from\":1}, or \"port\": true.","commonSituations":"Assuming the array form works because StringList accepts arrays — PortList does not; migrating configs from tools that emit arrays of ports.","solutions":["Use a string: \"port\": \"80,443,1000-2000\"","Or a single number: \"port\": 443","Do not use a JSON array for port lists"],"exampleFix":"// before\n\"port\": [80, 443]\n\n// after\n\"port\": \"80,443\"","handlingStrategy":"validation","validationCode":"func isPortListJSON(raw []byte) error {\n    var s string\n    if json.Unmarshal(raw, &s) == nil {\n        return nil\n    }\n    var n uint32\n    if json.Unmarshal(raw, &n) == nil {\n        return nil\n    }\n    return errors.New(\"port list must be a string like \\\"80,443,1000-2000\\\" or a number\")\n}","typeGuard":null,"tryCatchPattern":"if err := json.Unmarshal(data, &pl); err != nil {\n    if strings.Contains(err.Error(), \"invalid port\") {\n        return fmt.Errorf(\"routing port field %s: arrays are not supported; use a comma-separated string\", data)\n    }\n    return err\n}","preventionTips":["Never emit JSON arrays for routing 'port' fields","Prefer one combined string \"80,443,1000-2000\" over multiple rules"],"tags":["config","port","validation","xray"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}