{"record":{"id":"4c44de4d7349733e","repo":"fatedier/frp","slug":"client-id-s-for-user-s-is-already-online","errorCode":null,"errorMessage":"client_id [%s] for user [%s] is already online","messagePattern":"client_id \\[(.+?)\\] for user \\[(.+?)\\] is already online","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/control.go","lineNumber":213,"sourceCode":"\t}\n\n\tloginMsg := ctl.sessionCtx.LoginMsg\n\tremoteAddr := ctl.sessionCtx.Conn.RemoteAddr().String()\n\tif host, _, err := net.SplitHostPort(remoteAddr); err == nil {\n\t\tremoteAddr = host\n\t}\n\t_, conflict := cm.registry.RegisterWithControlID(\n\t\tloginMsg.User,\n\t\tloginMsg.ClientID,\n\t\tctl.runID,\n\t\tloginMsg.Hostname,\n\t\tloginMsg.Version,\n\t\tremoteAddr,\n\t\tctl.sessionCtx.WireProtocol,\n\t\tuint64(entry.id),\n\t)\n\tif conflict {\n\t\treturn true, fmt.Errorf(\"client_id [%s] for user [%s] is already online\", loginMsg.ClientID, loginMsg.User)\n\t}\n\n\tentry.registryOnline = true\n\tentry.registryControlID = entry.id\n\tctl.activated = true\n\treturn true, nil\n}\n\n// completeLogin reserves ctl's current ownership with its run gate while the\n// bounded successful LoginResp write runs, then transitions it to running.\n// The callback must only perform that bounded write; it must not call back into\n// the control manager or the same control lifecycle.\nfunc (cm *ControlManager) completeLogin(ctl *Control, writeSuccess func() error) (bool, error) {\n\tentry, ok := cm.lockCurrentRun(ctl.runID, false)\n\tif !ok {\n\t\treturn false, nil\n\t}\n\tdefer entry.runMu.Unlock()","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/server/control.go#L195-L231","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# frpc.toml — before (two instances share an id)\nclientID = \"box-1\"\n\n# after — unique per instance\nclientID = \"box-1-a\"","handlingStrategy":"validation","validationCode":"// server-side pre-check before login completes\nif _, online := registry.GetByID(user, loginMsg.ClientID); online {\n    return fmt.Errorf(\"client %s already online; pick a unique clientID\", loginMsg.ClientID)\n}","typeGuard":null,"tryCatchPattern":"if _, err := cm.Activate(ctl); err != nil {\n    if strings.Contains(err.Error(), \"already online\") {\n        // surface actionable message to the client/operator, terminate session\n    }\n}","preventionTips":["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."],"tags":["go","frp","registry","duplicate","session"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}