m1k1o/neko · warning
ErrIsAlreadyHosted
ErrIsAlreadyHosted
Error message
is already hosted
What it means
The declared sentinel ErrIsAlreadyHosted in server/internal/websocket/handler/control.go is returned by controlRequest when another session currently owns host control and the requester cannot take it (host is locked to that session). Neko keeps a single host at a time; control takeover requires the current host to release or be displaced under the configured policy.
Source
Thrown at server/internal/websocket/handler/control.go:16
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
}
func (h *MessageHandlerCtx) controlRequest(session types.Session) error {View on GitHub (pinned to b0f01cedea)
Solutions
- Wait for the current host to release, or ask them to release control.
- Have an admin use the admin host-transfer/kick controls to free the lock.
- Destroy the stale host session if it is dead (admin session list).
- Surface the current host's identity in the client UI so users know control is taken.
Example fix
// before
socket.on(event.CONTROL_REQUEST + 'error', () => retryTakeControl())
// after
socket.on(event.CONTROL_REQUEST + 'error', (e) => {
if (e.message === 'is already hosted') showHostTakenBanner()
}) Defensive patterns
Strategy: try-catch
Validate before calling
// client-side, before requesting control
if (state.hostId != null && state.hostId !== state.sessionId) {
showHostTakenBanner(state.hostId)
return
} Type guard
function isControlFree(state) {
return state.hostId == null
} Try / catch
try {
socket.emit('control/request')
} catch (e) {
if (e.message === 'is already hosted') {
showHostTakenBanner() // wait or ask current host to release
}
} Prevention
- Display the current host's name so users know control is taken.
- Don't auto-retry the request in a loop — wait for host/release events.
- Use admin controls to free a lock held by a dead session.
- Subscribe to control lock/release events to know when control becomes available.
When it happens
Trigger: Sending a control request while host != null and host != own session — i.e. someone else already controls the remote desktop and has not released it.
Common situations: Multiple participants clicking 'take control' concurrently; a host who walked away without releasing; integrations where the host lock was left held by a zombie session.
Related errors
AI-assisted analysis of m1k1o/neko@b0f01cedea (2026-09-01).
Data as JSON: /api/errors/3e7283e62c1c8358.
Report an issue: GitHub.