fatedier/frp · warning
client control for run id [%s] is no longer current
Error message
client control for run id [%s] is no longer current
What it means
RegisterWorkConn could not lock the current run for ctl.runID because the manager no longer has a matching entry (or the entry's run gate changed) and the manager is not closed — i.e. the control generation this work connection belongs to is gone. On error, conn ownership remains with the caller and the connection is closed there.
Source
Thrown at server/control.go:316
defer ctl.lifecycleMu.Unlock()
if ctl.state != controlStateRunning {
return false, nil
}
return true, admit(ctl.sessionCtx.LoginMsg.User, ctl.sessionCtx.WireProtocol, ctl.sessionCtx.UDPPacketCodec)
}
// RegisterWorkConn transfers conn to ctl only if ctl is still the current
// running generation. On error, ownership remains with the caller.
func (cm *ControlManager) RegisterWorkConn(ctl *Control, conn *proxy.WorkConn) error {
entry, ok := cm.lockCurrentRun(ctl.runID, false)
if !ok {
cm.mu.RLock()
closed := cm.closed
cm.mu.RUnlock()
if closed {
return fmt.Errorf("control manager is closed")
}
return fmt.Errorf("client control for run id [%s] is no longer current", ctl.runID)
}
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")View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Confirm only one frpc uses that run ID / client identity at a time.
- If embedding the server, treat this error as benign: close the work conn and let the new generation request its own work connections.
- Check frpc logs for a concurrent reconnect; align reconnect logic so the old session's work connections are torn down before new ones are created.
Defensive patterns
Strategy: fallback
Try / catch
if err := cm.RegisterWorkConn(ctl, conn); err != nil {
conn.Close() // caller retains ownership on error
if !strings.Contains(err.Error(), "no longer current") { log.Warn(err) }
} Prevention
- Never reuse a *Control handle across reconnect generations.
- Ensure only one live session per run ID to avoid generation swaps.
When it happens
Trigger: A new frpc login for the same run ID replaced the old control generation (Add swapped cm.ctlsByRunID[runID]), so the old generation's work connections no longer have a current entry; or the old control closed (e.g. after heartbeat timeout) and its entry was removed. A late NewWorkConn from the old session hits this path.
Common situations: frpc reconnecting with the same run ID (server restarted, connection dropped) while old work connections are still in flight; duplicate clients reusing a run ID; races between a control being replaced and frpc fulfilling an earlier ReqWorkConn.
Related errors
- client control for run id [%s] is not running
- work connection pool is full, discarding
- 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/c62d64880f9f5e25.
Report an issue: GitHub.