mgth/LittleBigMouse · error · IOException

VIDAA returned an invalid MQTT connection response.

Error message

VIDAA returned an invalid MQTT connection response.

What it means

Thrown in VidaaMqttConnection.ConnectAsync when the CONNACK packet returned by the Hisense VIDAA TV is not a valid MQTT connection acknowledgment: either the packet type (high nibble of the header) is not 2 (CONNACK) or the payload is shorter than 2 bytes. The library treats this as a protocol violation by the TV's MQTT broker rather than an authentication problem, so it surfaces as IOException. It means the device responded, but not with a recognizable MQTT handshake.

Solutions

  1. Verify the host and port point at the VIDAA TV's MQTT broker endpoint (not its HTTP API port).
  2. Power-cycle the TV and retry — a wedged broker can emit garbage instead of CONNACK.
  3. Confirm the TV model/firmware actually supports the VIDAA MQTT API this plugin targets; update or downgrade firmware if it changed the protocol.
  4. Capture traffic (or log the first bytes) on the socket to see what the TV actually returns before CONNACK is expected.

Example fix

// before: any exception bubbles up as-is
await connection.OpenMqttAsync(token);
// after: distinguish protocol failure from auth failure
try { await connection.OpenMqttAsync(token); }
catch (IOException e) when (e.Message.Contains("invalid MQTT connection response"))
{ /* wrong port/device or non-MQTT endpoint; fix host/port and retry */ }
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the endpoint speaks MQTT-TLS before connecting
canConnect = Uri.TryCreate(tvHost, UriKind.Absolute, out var uri) && uri.Port == mqttTlsPort;

Type guard

bool IsValidConnack(byte header, byte[] payload) => (header >> 4) == 2 && payload.Length >= 2;

Try / catch

try { await connection.OpenMqttAsync(token); }
catch (IOException e) when (e.Message.Contains("invalid MQTT connection response"))
{ // endpoint is not answering MQTT: fix host/port or firmware before retrying }

Prevention

When it happens

Trigger: ConnectAsync (called via OpenMqttAsync) reads the first packet after connecting to the TV's MQTT-over-TLS port; if ((connack.Header >> 4) != 2 || connack.Payload.Length < 2) the IOException is thrown — e.g. the TV replied with a plain-HTTP error page, an MQTT PUBACK instead of CONNACK, or a truncated/empty CONNACK.

Common situations: Wrong target port on the TV (connecting to an HTTP endpoint instead of the MQTT broker), a non-VIDAA device or non-standard firmware on that port, a proxy/middlebox mangling the TLS stream, or an incompatible VIDAA firmware version that returns a non-standard handshake response.

Related errors


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

Appendix: source

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

        {
            throw new AuthenticationException(
                "VIDAA rejected TLS. Select the PKCS#12 client certificate extracted from the official app.", e);
        }

        _stream = ssl;
        await WritePacketAsync(BuildConnectPacket(clientId, username, password), cancellationToken)
            .ConfigureAwait(false);
        (byte Header, byte[] Payload) connack;
        try
        {
            connack = await ReadPacketAsync(_stream, cancellationToken).ConfigureAwait(false);
        }
        catch (IOException e) when (certificates.Count == 0)
        {
            throw VidaaCertificate.MissingException(e);
        }
        if ((connack.Header >> 4) != 2 || connack.Payload.Length < 2)
            throw new IOException("VIDAA returned an invalid MQTT connection response.");
        if (connack.Payload[1] != 0)
            throw new UnauthorizedAccessException(MqttError(connack.Payload[1]));

        _connected = true;
        // The caller token limits the handshake only. Once established, MQTT has
        // its own lifetime so a completed UI command cannot tear down the session.
        _lifetime = new CancellationTokenSource();
        _readerTask = ReadLoopAsync(_lifetime.Token);
        _pingTask = PingLoopAsync(_lifetime.Token);
    }

    public async Task SubscribeAsync(IEnumerable<string> topics, CancellationToken cancellationToken)
    {
        foreach (var topic in topics)
        {
            var id = unchecked(++_packetId);
            if (id == 0) id = unchecked(++_packetId);
            using var body = new MemoryStream();

View on GitHub (pinned to 7a42f01d47)