{"record":{"id":"35e427f1ed792f82","repo":"fatedier/frp","slug":"port-already-used","errorCode":null,"errorMessage":"port already used","messagePattern":"port already used","errorType":"exception","errorClass":"ErrPortAlreadyUsed","httpStatus":null,"severity":"error","filePath":"server/ports/ports.go","lineNumber":23,"sourceCode":"\t\"net\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"k8s.io/utils/clock\"\n\n\t\"github.com/fatedier/frp/pkg/config/types\"\n)\n\nconst (\n\tMinPort                    = 1\n\tMaxPort                    = 65535\n\tMaxPortReservedDuration    = time.Duration(24) * time.Hour\n\tCleanReservedPortsInterval = time.Hour\n)\n\nvar (\n\tErrPortAlreadyUsed = errors.New(\"port already used\")\n\tErrPortNotAllowed  = errors.New(\"port not allowed\")\n\tErrPortUnAvailable = errors.New(\"port unavailable\")\n\tErrNoAvailablePort = errors.New(\"no available port\")\n)\n\ntype PortCtx struct {\n\tProxyName  string\n\tPort       int\n\tClosed     bool\n\tUpdateTime time.Time\n}\n\ntype Manager struct {\n\treservedPorts map[string]*PortCtx\n\tusedPorts     map[int]*PortCtx\n\tfreePorts     map[int]struct{}\n\n\tbindAddr string","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/server/ports/ports.go#L5-L41","documentation":"ErrPortAlreadyUsed is returned by ports.Manager.Acquire when the requested remote port is already held in usedPorts — i.e. another proxy on this frps has already acquired it. It distinguishes 'taken by another frp proxy' from ports blocked at the OS level (ErrPortUnAvailable) and ports outside the allowed range (ErrPortNotAllowed).","triggerScenarios":"Two clients (or two proxies) requesting the same fixed remotePort; a previous proxy still holding the port after a client reconnect race; a STCP/similar proxy keeping the port reserved.","commonSituations":"Cloned frpc instances with identical remotePort; conflicting proxy definitions across teams sharing one frps; rapid reconnects where the old proxy's port has not been released yet.","solutions":["Change one proxy's remotePort to a free value, or set remotePort = 0 to let frps assign a random free port","If the previous holder is gone, wait for frps to release it on connection close, or restart frps to clear stale state","List running proxies on the frps dashboard to find the current owner of the port","For many identical clients, use load-balance groups (same group+groupKey+port) instead of duplicate fixed ports"],"exampleFix":"# before — two proxies both use 6000\n[[proxies]]\nname = \"ssh-b\"\ntype = \"tcp\"\nremotePort = 6000\n\n# after\n[[proxies]]\nname = \"ssh-b\"\ntype = \"tcp\"\nremotePort = 6001","handlingStrategy":"validation","validationCode":"// frps side: pre-check before Acquire\nif _, used := pm.CheckPortUsed(port); used { // or query your inventory\n    return fmt.Errorf(\"remotePort %d already taken — choose another\", port)\n}","typeGuard":"func isPortAlreadyUsed(err error) bool {\n    return errors.Is(err, ports.ErrPortAlreadyUsed)\n}","tryCatchPattern":"realPort, err := portManager.Acquire(name, port)\nif errors.Is(err, ports.ErrPortAlreadyUsed) {\n    // another frp proxy holds it: pick a different fixed port or use remotePort = 0\n}","preventionTips":["Track fixed remotePort allocations in a config registry so two proxies never claim the same one","Prefer remotePort = 0 for autoscaled fleets and read back the assigned port from the admin API"],"tags":["frp","go","port","server","conflict"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}