{"record":{"id":"ade75038e38704b6","repo":"fatedier/frp","slug":"local-ports-number-should-be-same-with-remote-port","errorCode":null,"errorMessage":"local ports number should be same with remote ports number","messagePattern":"local ports number should be same with remote ports number","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/legacy/client.go","lineNumber":308,"sourceCode":"\t// Validation\n\tlocalPortStr := section.Key(\"local_port\").String()\n\tremotePortStr := section.Key(\"remote_port\").String()\n\tif localPortStr == \"\" || remotePortStr == \"\" {\n\t\treturn fmt.Errorf(\"local_port or remote_port is empty\")\n\t}\n\n\tlocalPorts, err := util.ParseRangeNumbers(localPortStr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tremotePorts, err := util.ParseRangeNumbers(remotePortStr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(localPorts) != len(remotePorts) {\n\t\treturn fmt.Errorf(\"local ports number should be same with remote ports number\")\n\t}\n\n\tif len(localPorts) == 0 {\n\t\treturn fmt.Errorf(\"local_port and remote_port is necessary\")\n\t}\n\n\t// Templates\n\tprefix := strings.TrimSpace(strings.TrimPrefix(section.Name(), \"range:\"))\n\n\tfor i := range localPorts {\n\t\ttmpname := fmt.Sprintf(\"%s_%d\", prefix, i)\n\n\t\ttmpsection, err := f.NewSection(tmpname)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tcopySection(section, tmpsection)","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/legacy/client.go#L290-L326","documentation":"Range-proxy validation: after util.ParseRangeNumbers expands both sides (single numbers, comma lists, and a-b ranges), the number of local ports must equal the number of remote ports so each local port pairs with exactly one remote port. A count mismatch aborts expansion, later surfacing as 'failed to render template for proxy ...' (error 151).","triggerScenarios":"local_port = '6000-6002' (3 ports) with remote_port = '7000-7001' (2 ports); or local_port = '6000,6001,6002' with remote_port = '7000,7002'. The error fires before any sections are generated.","commonSituations":"Editing one side of a range pair and forgetting the other; assuming frp cycles or truncates the shorter list; mixing a range on one side with a single value on the other.","solutions":["Count the ports each side expands to and make them equal","For an a-b range of N ports, give the other side N values too (e.g. matching a-b range)","Prefer symmetric ranges (6000-6002 / 7000-7002) to avoid manual counting"],"exampleFix":"; before\n[range:web]\ntype = tcp\nlocal_port = 6000-6003   ; 4 ports\nremote_port = 7000-7002   ; 3 ports\n\n; after\n[range:web]\ntype = tcp\nlocal_port = 6000-6003\nremote_port = 7000-7003","handlingStrategy":"validation","validationCode":"lp, err := util.ParseRangeNumbers(localStr); if err != nil { return err }\nrp, err := util.ParseRangeNumbers(remoteStr); if err != nil { return err }\nif len(lp) != len(rp) {\n    return fmt.Errorf(\"local expands to %d ports, remote to %d; must match\", len(lp), len(rp))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Count entries on both sides when mixing ranges and comma lists","Prefer matching a-b ranges (6000-6002 / 7000-7002)","Script a count check in CI for range sections"],"tags":["frp","config","ini","range-proxy","port-mapping"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}