m1k1o/neko · warning

ErrIsAlreadyTheHost

ErrIsAlreadyTheHost

Error message

is already the host

What it means

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.

Source

Thrown at server/internal/websocket/handler/control.go:15

package handler

import (
	"errors"

	"github.com/m1k1o/neko/server/pkg/types"
	"github.com/m1k1o/neko/server/pkg/types/event"
	"github.com/m1k1o/neko/server/pkg/types/message"
	"github.com/m1k1o/neko/server/pkg/xorg"
)

var (
	ErrIsNotAllowedToHost = errors.New("is not allowed to host")
	ErrIsNotTheHost       = errors.New("is not the host")
	ErrIsAlreadyTheHost   = errors.New("is already the host")
	ErrIsAlreadyHosted    = errors.New("is already hosted")
)

func (h *MessageHandlerCtx) controlRelease(session types.Session) error {
	if !session.Profile().CanHost || session.PrivateModeEnabled() {
		return ErrIsNotAllowedToHost
	}

	if !session.IsHost() {
		return ErrIsNotTheHost
	}

	h.desktop.ResetKeys()
	session.ClearHost()

	return nil
}

View on GitHub (pinned to b0f01cedea)

Solutions

  1. Treat ErrIsAlreadyTheHost as success client-side: you already have control.
  2. Disable the take-control action when the client knows it is the host.
  3. Debounce/deduplicate control requests in the client.
  4. Just send input events directly instead of re-requesting control.

Example fix

// before
socket.send(event.CONTROL_REQUEST) // on every mouse move if not locked
// after
if (!state.isHost) socket.send(event.CONTROL_REQUEST)
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side, before requesting control
if (state.isHost) {
  return // already the host, no need to request
}

Type guard

function shouldRequestControl(state) {
  return state.hostId !== state.sessionId
}

Try / catch

try {
  socket.emit('control/request')
} catch (e) {
  if (e.message === 'is already the host') {
    state.isHost = true // treat as success
  }
}

Prevention

When it happens

Trigger: Sending a control request (take) while already the host; calling host-gated input operations through paths that validate ErrIsAlreadyTheHost in conflicting take scenarios.

Common situations: 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.

Related errors


AI-assisted analysis of m1k1o/neko@b0f01cedea (2026-09-01). Data as JSON: /api/errors/70807e6e193aaa84. Report an issue: GitHub.