{"record":{"id":"70807e6e193aaa84","repo":"m1k1o/neko","slug":"errisalreadythehost","errorCode":"ErrIsAlreadyTheHost","errorMessage":"is already the host","messagePattern":"is already the host","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/internal/websocket/handler/control.go","lineNumber":15,"sourceCode":"package handler\n\nimport (\n\t\"errors\"\n\n\t\"github.com/m1k1o/neko/server/pkg/types\"\n\t\"github.com/m1k1o/neko/server/pkg/types/event\"\n\t\"github.com/m1k1o/neko/server/pkg/types/message\"\n\t\"github.com/m1k1o/neko/server/pkg/xorg\"\n)\n\nvar (\n\tErrIsNotAllowedToHost = errors.New(\"is not allowed to host\")\n\tErrIsNotTheHost       = errors.New(\"is not the host\")\n\tErrIsAlreadyTheHost   = errors.New(\"is already the host\")\n\tErrIsAlreadyHosted    = errors.New(\"is already hosted\")\n)\n\nfunc (h *MessageHandlerCtx) controlRelease(session types.Session) error {\n\tif !session.Profile().CanHost || session.PrivateModeEnabled() {\n\t\treturn ErrIsNotAllowedToHost\n\t}\n\n\tif !session.IsHost() {\n\t\treturn ErrIsNotTheHost\n\t}\n\n\th.desktop.ResetKeys()\n\tsession.ClearHost()\n\n\treturn nil\n}\n","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/m1k1o/neko/blob/b0f01cedea68893e85a3fd852c0521238c285695/server/internal/websocket/handler/control.go#L1-L33","documentation":"The declared sentinel ErrIsAlreadyTheHost in server/internal/websocket/handler/control.go is returned by controlRequest and by control input handlers (controlMove, controlScroll, controlButtonPress, controlButtonDown, controlButtonUp) when the session attempts to take or implicitly use host actions it already holds. Host transfer (take) is refused when the requesting session already owns control.","triggerScenarios":"Sending a control request (take) while already the host; calling host-gated input operations through paths that validate ErrIsAlreadyTheHost in conflicting take scenarios.","commonSituations":"Double-clicking the 'take control' button; an auto-control-on-interaction client sending a request on every input event; a UI not updating after a successful take.","solutions":["Treat ErrIsAlreadyTheHost as success client-side: you already have control.","Disable the take-control action when the client knows it is the host.","Debounce/deduplicate control requests in the client.","Just send input events directly instead of re-requesting control."],"exampleFix":"// before\nsocket.send(event.CONTROL_REQUEST) // on every mouse move if not locked\n// after\nif (!state.isHost) socket.send(event.CONTROL_REQUEST)","handlingStrategy":"try-catch","validationCode":"// client-side, before requesting control\nif (state.isHost) {\n  return // already the host, no need to request\n}","typeGuard":"function shouldRequestControl(state) {\n  return state.hostId !== state.sessionId\n}","tryCatchPattern":"try {\n  socket.emit('control/request')\n} catch (e) {\n  if (e.message === 'is already the host') {\n    state.isHost = true // treat as success\n  }\n}","preventionTips":["Debounce/deduplicate control requests in the client.","Treat this error as confirmation of ownership, not a failure.","Update isHost state immediately after a successful take.","Auto-control-on-input clients should check isHost before sending requests."],"tags":["websocket","host-control","duplicate-request"],"backgroundTag":"already-the-host","analyzedSha":"b0f01cedea68893e85a3fd852c0521238c285695","analyzedAt":"2026-09-01T10:35:56.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}