mgth/LittleBigMouse · error · IOException

The VIDAA MQTT connection is closed.

Error message

The VIDAA MQTT connection is closed.

What it means

Thrown by WritePacketAsync when attempting to write an MQTT packet while _stream is null, meaning the connection has not been opened or has already been closed/disposed. The library guards the write with an IOException so callers get a clear 'connection closed' signal instead of a NullReferenceException. Any operation that sends packets (connect handshake, ping loop, command writes) will hit this if the session is not alive.

Solutions

  1. Call OpenMqttAsync (which runs ConnectAsync) before sending any packets, and check IsConnected/_connected first if available.
  2. Reopen the connection after a close: wrap sends so that on IOException the connection is re-established and the command retried.
  3. Serialize access: don't dispose/close the connection while writes are in flight; await in-flight commands before closing.
  4. Handle TV-side disconnects (reboot, sleep) by subscribing to the read-loop completion and marking the connection dead instead of reusing it.

Example fix

// before: writes assume the connection is always open
await connection.WriteMqttPacketAsync(packet, token);
// after: ensure/reopen the connection before writing
if (!connection.IsConnected) await connection.OpenMqttAsync(token);
await connection.WriteMqttPacketAsync(packet, token);
Defensive patterns

Strategy: validation

Validate before calling

// Check connection state before writing
if (_stream is null || !IsConnected)
    throw new InvalidOperationException("Open the MQTT connection (OpenMqttAsync) before sending packets.");

Type guard

bool CanWrite(VidaaMqttConnection c) => c is { IsConnected: true };

Try / catch

try { await connection.WriteMqttPacketAsync(packet, token); }
catch (IOException e) when (e.Message.Contains("connection is closed"))
{ await connection.OpenMqttAsync(token); await connection.WriteMqttPacketAsync(packet, token); }

Prevention

When it happens

Trigger: WritePacketAsync is called by ConnectAsync, PingLoopAsync, or WriteMqttPacketAsync while _stream is null — i.e. writing before OpenMqttAsync completed, after Close/Dispose, or after the read loop detected the TV dropped the connection and cleared _stream.

Common situations: Sending a mouse/VCP command after the TV rebooted or the Wi-Fi dropped; a long-lived connection object reused after Dispose; concurrent commands racing with an implicit disconnect; forgetting to call OpenMqttAsync before the first write.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at LittleBigMouse.Plugins/LittleBigMouse.Plugin.Vcp.Avalonia/HisenseVidaa/VidaaMqttConnection.cs:197

        catch (Exception e)
        {
            Console.Error.WriteLine($"VIDAA MQTT keepalive stopped: {e.Message}");
            MarkDisconnected();
        }
    }

    async Task WriteMqttPacketAsync(byte header, byte[] body, CancellationToken cancellationToken)
    {
        using var packet = new MemoryStream();
        packet.WriteByte(header);
        WriteRemainingLength(packet, body.Length);
        packet.Write(body);
        await WritePacketAsync(packet.ToArray(), cancellationToken).ConfigureAwait(false);
    }

    async Task WritePacketAsync(byte[] packet, CancellationToken cancellationToken)
    {
        var stream = _stream ?? throw new IOException("The VIDAA MQTT connection is closed.");
        await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
        try
        {
            await stream.WriteAsync(packet, cancellationToken).ConfigureAwait(false);
            await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
        }
        catch
        {
            MarkDisconnected();
            throw;
        }
        finally
        {
            _writeLock.Release();
        }
    }

    internal static byte[] BuildConnectPacket(string clientId, string username, string password)

View on GitHub (pinned to 7a42f01d47)