mgth/LittleBigMouse · error · WebSocketException

The Samsung display closed the remote-control channel.

Error message

The Samsung display closed the remote-control channel.

What it means

SamsungTizenClient.ReceiveTextLockedAsync reads WebSocket text frames from the display's remote-control channel. When the socket reports a Close frame instead of a text message, the client throws WebSocketException to signal that the Samsung display terminated the session. The library surfaces this as an exception rather than returning null so callers must explicitly re-connect or re-authorize.

Solutions

  1. Catch WebSocketException around receive/send calls and recreate the SamsungTizenClient, re-running the connection/handshake
  2. Check whether the display still shows the client as an allowed device and re-authorize if the token was revoked
  3. Send periodic keep-alive/ping frames to prevent idle disconnects
  4. Handle the Close frame by surfacing a user-facing 'display disconnected' state and retrying with backoff

Example fix

// before
var text = await client.ReceiveTextLockedAsync(ct);
// after
try { var text = await client.ReceiveTextLockedAsync(ct); }
catch (WebSocketException)
{
    await client.ReconnectAsync(ct); // recreate socket and re-handshake with the display
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Check socket state before receiving
if (client is not { IsConnected: true })
    await ReconnectAsync(ct);

Type guard

static bool IsUsable(SamsungTizenClient? c) => c is { IsConnected: true };

Try / catch

try
{
    var text = await client.ReceiveTextLockedAsync(ct);
}
catch (WebSocketException ex)
{
    // display closed the channel — reconnect and re-authorize
    await ReconnectWithBackoffAsync(ct);
}

Prevention

When it happens

Trigger: Calling ReceiveTextLockedAsync (via the client's message loop) while the Samsung display sends a WebSocket Close frame — e.g. after the display revokes the remote-control session, times out idle connections, or the user rejects/ends control on the TV.

Common situations: The TV was turned off or rebooted mid-session; another client (phone app) took over the remote-control slot; the token was revoked in the TV's device-connection settings; the WebSocket sat idle until the display dropped it.

Related errors


AI-assisted analysis of mgth/LittleBigMouse@7a42f01d47 (2026-09-16). Data as JSON: /api/errors/9256a330f548f285. Report an issue: GitHub.

Appendix: source

Thrown at LittleBigMouse.Plugins/LittleBigMouse.Plugin.Vcp.Avalonia/SamsungTizen/SamsungTizenClient.cs:147

    void ResetSocketLocked()
    {
        if (_socket is null) return;
        try { _socket.Abort(); }
        catch { }
        _socket.Dispose();
        _socket = null;
    }

    async Task<string> ReceiveTextLockedAsync(CancellationToken cancellationToken)
    {
        var buffer = new byte[4096];
        using var message = new MemoryStream();

        while (true)
        {
            var result = await _socket!.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false);
            if (result.MessageType == WebSocketMessageType.Close)
                throw new WebSocketException("The Samsung display closed the remote-control channel.");
            if (result.MessageType != WebSocketMessageType.Text) continue;

            message.Write(buffer, 0, result.Count);
            if (result.EndOfMessage) return Encoding.UTF8.GetString(message.GetBuffer(), 0, checked((int)message.Length));
        }
    }

    public async ValueTask DisposeAsync()
        => await _commands.CloseAsync(async () =>
        {
            if (_socket?.State == WebSocketState.Open)
            {
                using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1));
                try
                {
                    await _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "LittleBigMouse closing", timeout.Token)
                        .ConfigureAwait(false);
                }

View on GitHub (pinned to 7a42f01d47)