fatedier/frp · error
client_id [%s] for user [%s] is already online
Error message
client_id [%s] for user [%s] is already online
What it means
ControlManager.Activate calls ClientRegistry.RegisterWithControlID with the login message's User and ClientID; when the registry already holds an online entry for that (user, clientID) pair from a different run, it reports a conflict and activation fails. This is frps enforcing one online session per client identity.
Source
Thrown at server/control.go:213
}
loginMsg := ctl.sessionCtx.LoginMsg
remoteAddr := ctl.sessionCtx.Conn.RemoteAddr().String()
if host, _, err := net.SplitHostPort(remoteAddr); err == nil {
remoteAddr = host
}
_, conflict := cm.registry.RegisterWithControlID(
loginMsg.User,
loginMsg.ClientID,
ctl.runID,
loginMsg.Hostname,
loginMsg.Version,
remoteAddr,
ctl.sessionCtx.WireProtocol,
uint64(entry.id),
)
if conflict {
return true, fmt.Errorf("client_id [%s] for user [%s] is already online", loginMsg.ClientID, loginMsg.User)
}
entry.registryOnline = true
entry.registryControlID = entry.id
ctl.activated = true
return true, nil
}
// completeLogin reserves ctl's current ownership with its run gate while the
// bounded successful LoginResp write runs, then transitions it to running.
// The callback must only perform that bounded write; it must not call back into
// the control manager or the same control lifecycle.
func (cm *ControlManager) completeLogin(ctl *Control, writeSuccess func() error) (bool, error) {
entry, ok := cm.lockCurrentRun(ctl.runID, false)
if !ok {
return false, nil
}
defer entry.runMu.Unlock()View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Ensure each frpc instance has a unique client ID in its config (auth or common section), or omit the client id so it is derived uniquely.
- If it is a stale session, let the old connection time out (heartbeat timeout) or restart the conflicting frpc; the registry slot frees once the old control closes.
- Check the frps dashboard/API for the online client holding that ID and kill that session if it is truly dead.
- If duplicate identities are expected in your deployment, put them under different users.
Example fix
# frpc.toml — before (two instances share an id) clientID = "box-1" # after — unique per instance clientID = "box-1-a"
Defensive patterns
Strategy: validation
Validate before calling
// server-side pre-check before login completes
if _, online := registry.GetByID(user, loginMsg.ClientID); online {
return fmt.Errorf("client %s already online; pick a unique clientID", loginMsg.ClientID)
} Try / catch
if _, err := cm.Activate(ctl); err != nil {
if strings.Contains(err.Error(), "already online") {
// surface actionable message to the client/operator, terminate session
}
} Prevention
- Assign every frpc instance a unique clientID in config.
- Monitor the frps dashboard for duplicate identities before deploying another instance with the same config.
- Automate config generation so clientIDs embed hostname or role.
When it happens
Trigger: A second frpc process (or a reconnecting frpc whose old TCP session is still registered) logs in with the same user + client ID while the previous session has not yet been cleaned up. RegisterWithControlID returns conflict=true and Activate returns this error, which typically terminates the new login.
Common situations: Running two frpc instances with a copied config (same client id) against the same server; a stale/half-dead TCP connection from a network drop whose server-side cleanup has not completed; NAT rebinding or quick reconnects after connectivity loss.
Related errors
- exec configuration is required when type is 'exec'
- file path cannot be empty
- exec command cannot be empty
- exec env name cannot be empty
- exec env name cannot contain '='
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/4c44de4d7349733e.
Report an issue: GitHub.