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 string

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Change one proxy's remotePort to a free value, or set remotePort = 0 to let frps assign a random free port
  2. If the previous holder is gone, wait for frps to release it on connection close, or restart frps to clear stale state
  3. List running proxies on the frps dashboard to find the current owner of the port
  4. 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

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


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/35e427f1ed792f82. Report an issue: GitHub.