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
- Call OpenMqttAsync (which runs ConnectAsync) before sending any packets, and check IsConnected/_connected first if available.
- Reopen the connection after a close: wrap sends so that on IOException the connection is re-established and the command retried.
- Serialize access: don't dispose/close the connection while writes are in flight; await in-flight commands before closing.
- 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
- Always open the connection before the first command and after any close/dispose
- Track read-loop completion to mark the connection dead instead of reusing it
- Serialize writes with the connection lifetime; never dispose while writes are pending
- Wrap command sends in a reconnect-once helper
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
- VIDAA closed the MQTT connection.
- VIDAA returned an invalid MQTT connection response.
- MqttError(connack.Payload[1])
- Malformed MQTT remaining length.
- Enter a platform action name containing only letters…
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)