fatedier/frp · error · ErrPortUnAvailable
port unavailable
Error message
port unavailable
What it means
ErrPortUnAvailable is returned by ports.Manager.Acquire when the port is inside frps's allowed free pool but the live availability check fails — ports.Manager.isPortAvailable actually attempts net.Listen (or ListenUDP for udp) on the bind address, so the port is occupied at the OS level by a non-frp process, or the bind would fail (permissions, address in use).
Source
Thrown at server/ports/ports.go:25
"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
netType string
clock clock.WithTickerView on GitHub (pinned to 6c8a8d0a97)
Solutions
- On the frps host run ss -ltnp / ss -ulnp to find the process holding the port and stop it or pick another port
- Choose a remotePort outside the kernel's ephemeral port range (cat /proc/sys/net/ipv4/ip_local_port_range)
- For ports < 1024, run frps with CAP_NET_BIND_SERVICE or as root, or use a higher port
- Retry after freeing the port — the check is live, so it passes once the port is actually bindable
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight on the frps host: confirm the port is actually bindable
func portBindable(network, bindAddr string, port int) bool {
l, err := net.Listen(network, net.JoinHostPort(bindAddr, strconv.Itoa(port)))
if err != nil {
return false
}
l.Close()
return true
} Type guard
func isPortUnavailable(err error) bool {
return errors.Is(err, ports.ErrPortUnAvailable)
} Try / catch
realPort, err := pm.Acquire(name, port)
if errors.Is(err, ports.ErrPortUnAvailable) {
// OS-level conflict: find and free the port, or choose another; a retry after freeing succeeds
} Prevention
- Keep frps hosts dedicated to frp; scan for stray listeners after any co-hosted deployment
- Exclude the kernel ephemeral range and ports below 1024 from allowPorts unless frps is privileged
When it happens
Trigger: Another process on the frps host already listens on the requested port (nginx, sshd, another frps); requesting a privileged port below 1024 while frps runs unprivileged; UDP vs TCP type mismatch where the check protocol differs from the proxy's actual use; ephemeral-port collisions.
Common situations: frps host co-hosts other services; kernel ephemeral range overlapping allowPorts; SELinux/AppArmor denying the bind; IPv6/IPv4 bind address mismatch between the checker and the listener.
Related errors
- group should have same remote port
- port already used
- port not allowed
- send ${op} request to plugin error
- no route found
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/2e09207076332737.
Report an issue: GitHub.