fatedier/frp · error · ErrPortAlreadyUsed
port already used
Error message
port already used
What it means
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).
Source
Thrown at server/ports/ports.go:23
"net"
"strconv"
"sync"
"time"
"k8s.io/utils/clock"
"github.com/fatedier/frp/pkg/config/types"
)
const (
MinPort = 1
MaxPort = 65535
MaxPortReservedDuration = time.Duration(24) * time.Hour
CleanReservedPortsInterval = time.Hour
)
var (
ErrPortAlreadyUsed = errors.New("port already used")
ErrPortNotAllowed = errors.New("port not allowed")
ErrPortUnAvailable = errors.New("port unavailable")
ErrNoAvailablePort = errors.New("no available port")
)
type PortCtx struct {
ProxyName string
Port int
Closed bool
UpdateTime time.Time
}
type Manager struct {
reservedPorts map[string]*PortCtx
usedPorts map[int]*PortCtx
freePorts map[int]struct{}
bindAddr stringView on GitHub (pinned to 6c8a8d0a97)
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
Example fix
# before — two proxies both use 6000 [[proxies]] name = "ssh-b" type = "tcp" remotePort = 6000 # after [[proxies]] name = "ssh-b" type = "tcp" remotePort = 6001
Defensive patterns
Strategy: validation
Validate before calling
// frps side: pre-check before Acquire
if _, used := pm.CheckPortUsed(port); used { // or query your inventory
return fmt.Errorf("remotePort %d already taken — choose another", port)
} Type guard
func isPortAlreadyUsed(err error) bool {
return errors.Is(err, ports.ErrPortAlreadyUsed)
} Try / catch
realPort, err := portManager.Acquire(name, port)
if errors.Is(err, ports.ErrPortAlreadyUsed) {
// another frp proxy holds it: pick a different fixed port or use remotePort = 0
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- router config conflict
- group should have same remote port
- port not allowed
- port unavailable
- send ${op} request to plugin error
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/35e427f1ed792f82.
Report an issue: GitHub.