fatedier/frp · info
work connection pool is full, discarding
Error message
work connection pool is full, discarding
What it means
ctl.workConnCh is a buffered channel sized poolCount + workConnPoolCapacityOffset. RegisterWorkConn uses a non-blocking select; if the buffer is full the connection is discarded with this error. This is intentional backpressure: the server already has as many idle work connections as the client's pool allows.
Source
Thrown at server/control.go:335
}
defer entry.runMu.Unlock()
if entry.ctl != ctl || entry.id != ctl.controlID {
return fmt.Errorf("client control for run id [%s] is no longer current", ctl.runID)
}
ctl.lifecycleMu.Lock()
defer ctl.lifecycleMu.Unlock()
if ctl.state != controlStateRunning {
return fmt.Errorf("client control for run id [%s] is not running", ctl.runID)
}
select {
case ctl.workConnCh <- conn:
ctl.xl.Debugf("new work connection registered")
return nil
default:
ctl.xl.Debugf("work connection pool is full, discarding")
return fmt.Errorf("work connection pool is full, discarding")
}
}
func (cm *ControlManager) Close() error {
cm.mu.Lock()
cm.closed = true
ctls := make([]*Control, 0, len(cm.ctlsByRunID))
for _, entry := range cm.ctlsByRunID {
ctls = append(ctls, entry.ctl)
}
cm.mu.Unlock()
for _, ctl := range ctls {
cm.Remove(ctl)
_ = ctl.Close()
}
return nil
}View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Tune frpc's transport.poolCount (and server maxPoolCount) to match your connection concurrency.
- It is debug-level and expected under bursts — no action needed if frequency is low; frpc will reopen work connections on demand.
- If it floods logs, lower pool count or reduce connection churn (enable TCP mux for user conns where applicable).
Example fix
# frpc.toml [transport] poolCount = 5 # size to expected concurrent user connections
Defensive patterns
Strategy: fallback
Validate before calling
// client side: only open what was requested
if inflightWorkConns >= poolCount { return /* skip opening more */ } Try / catch
if err := cm.RegisterWorkConn(ctl, conn); err != nil {
conn.Close()
// full pool is expected under bursts; log at debug only
} Prevention
- Size transport.poolCount to real concurrency.
- Only open work connections in response to ReqWorkConn, not speculatively.
When it happens
Trigger: frpc sends more NewWorkConn messages than the negotiated pool size — typically because the server requested work connections (ReqWorkConn) for user traffic, but the traffic finished before frpc's response arrived, leaving surplus connections. The default branch of the select fires and the conn is dropped.
Common situations: Bursty proxied traffic where many short-lived user connections each trigger ReqWorkConn; frpc with a large PoolCount plus latency spikes; clients that eagerly open work connections beyond the pool.
Related errors
- client control for run id [%s] is no longer current
- client control for run id [%s] is not running
- control is already closed
- exec configuration is required when type is 'exec'
- file path cannot be empty
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/58e4cd7a00f45a12.
Report an issue: GitHub.