{"record":{"id":"9256a330f548f285","repo":"mgth/LittleBigMouse","slug":"the-samsung-display-closed-the-remote-control-channel","errorCode":null,"errorMessage":"The Samsung display closed the remote-control channel.","messagePattern":"The Samsung display closed the remote-control channel\\.","errorType":"exception","errorClass":"WebSocketException","httpStatus":null,"severity":"error","filePath":"LittleBigMouse.Plugins/LittleBigMouse.Plugin.Vcp.Avalonia/SamsungTizen/SamsungTizenClient.cs","lineNumber":147,"sourceCode":"    void ResetSocketLocked()\n    {\n        if (_socket is null) return;\n        try { _socket.Abort(); }\n        catch { }\n        _socket.Dispose();\n        _socket = null;\n    }\n\n    async Task<string> ReceiveTextLockedAsync(CancellationToken cancellationToken)\n    {\n        var buffer = new byte[4096];\n        using var message = new MemoryStream();\n\n        while (true)\n        {\n            var result = await _socket!.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false);\n            if (result.MessageType == WebSocketMessageType.Close)\n                throw new WebSocketException(\"The Samsung display closed the remote-control channel.\");\n            if (result.MessageType != WebSocketMessageType.Text) continue;\n\n            message.Write(buffer, 0, result.Count);\n            if (result.EndOfMessage) return Encoding.UTF8.GetString(message.GetBuffer(), 0, checked((int)message.Length));\n        }\n    }\n\n    public async ValueTask DisposeAsync()\n        => await _commands.CloseAsync(async () =>\n        {\n            if (_socket?.State == WebSocketState.Open)\n            {\n                using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1));\n                try\n                {\n                    await _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, \"LittleBigMouse closing\", timeout.Token)\n                        .ConfigureAwait(false);\n                }","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/mgth/LittleBigMouse/blob/7a42f01d47d99d223b8ee33ba4019af82adf1c48/LittleBigMouse.Plugins/LittleBigMouse.Plugin.Vcp.Avalonia/SamsungTizen/SamsungTizenClient.cs#L129-L165","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Catch WebSocketException around receive/send calls and recreate the SamsungTizenClient, re-running the connection/handshake","Check whether the display still shows the client as an allowed device and re-authorize if the token was revoked","Send periodic keep-alive/ping frames to prevent idle disconnects","Handle the Close frame by surfacing a user-facing 'display disconnected' state and retrying with backoff"],"exampleFix":"// before\nvar text = await client.ReceiveTextLockedAsync(ct);\n// after\ntry { var text = await client.ReceiveTextLockedAsync(ct); }\ncatch (WebSocketException)\n{\n    await client.ReconnectAsync(ct); // recreate socket and re-handshake with the display\n}","handlingStrategy":"try-catch","validationCode":"// Check socket state before receiving\nif (client is not { IsConnected: true })\n    await ReconnectAsync(ct);","typeGuard":"static bool IsUsable(SamsungTizenClient? c) => c is { IsConnected: true };","tryCatchPattern":"try\n{\n    var text = await client.ReceiveTextLockedAsync(ct);\n}\ncatch (WebSocketException ex)\n{\n    // display closed the channel — reconnect and re-authorize\n    await ReconnectWithBackoffAsync(ct);\n}","preventionTips":["Send periodic WebSocket pings/keep-alives to avoid idle timeouts","Re-authorize the client in the TV's device settings if sessions are revoked","Monitor connection state and recreate the client proactively on failures","Retry with exponential backoff instead of immediate reconnect loops"],"tags":["websocket","network","samsung-tizen","disconnection"],"backgroundTag":"broken-pipe","analyzedSha":"7a42f01d47d99d223b8ee33ba4019af82adf1c48","analyzedAt":"2026-09-16T00:35:00.514Z","contentChangedAt":"2026-09-16T00:35:00.514Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}