MQTT client closes a recoverable TCP connection before retransmission recovery
PROBLEM DESCRIPTION
Tasmota can terminate an MQTT connection while the underlying TCP connection is still potentially recoverable through normal TCP retransmission.
This report is not asking Tasmota to fix a particular Wi-Fi/AP packet-loss condition. Packet loss is the trigger that makes the behavior reproducible. The software robustness issue is the timeout interaction:
the MQTT client gives up before TCP retransmission recovery has completed.
Observed sequence:
- Tasmota sends MQTT PINGREQ.
- Mosquitto replies with the 2-byte MQTT PINGRESP.
- The broker does not receive a TCP ACK for that short response; Wi-Fi remains associated and the device Wi-Fi connect counter does not increase.
- Linux TCP performs retransmission recovery. Under normal exponential RTO backoff for a thin stream, a later retransmission was delayed by approximately 113-116 seconds.
- With MqttKeepAlive 100, the Tasmota MQTT client closes the socket before that late retransmission can complete delivery.
- Mosquitto logs "connection closed by client"; the device reconnects and MqttCount increases.
The result is that a connection which TCP could still recover is instead terminated at the MQTT layer.
The problem is currently mitigated in production by enabling net.ipv4.tcp_thin_linear_timeouts = 1 on the Linux broker host. This is why no new failure is being intentionally induced for a device-side weblog 4 capture: disabling a working mitigation merely to reproduce the fault would reintroduce avoidable MQTT disconnects. The passive controlled experiment below is the evidence for the pre-mitigation behavior.
REQUESTED INFORMATION
Read the Contributing Guide and Policy and the Code of Conduct
Searched the problem in issues: no matching open issue was found.
Searched the problem in discussions: no matching discussion was found.
Searched the FAQ, including “Frequent MQTT reconnects”. The documented duplicate-client and Wi-Fi remedies do not explain this packet-level sequence: the devices retained unique topics and Wi-Fi LinkCount did not increase.
Searched the problem in the chat: not performed; no claim is made that Discord was searched.
Problem is not scripter related
Device used: ESP-01S Relay (ESP8266EX, 1 MiB flash); Athom Plug V2 (ESP8285H16, 2 MiB flash); and Athom Mini Relay V2 (ESP32-C3 v0.4, 4 MiB flash), used as the bedroom AC power relay. The same MQTT-reconnect symptom was observed across the reporter's Tasmota devices, including ESP8266 and ESP32-C3 hardware.
Tasmota binary firmware version number used: 15.5.0(release-tasmota), build 2026-06-22T13:58:14. A latest-development-build retest has not yet been performed.
Pre-compiled
Self-compiled (not applicable: all three representative devices use pre-compiled firmware).
Flashing tools used: not relevant to the observation; pre-compiled firmware was already installed and no flashing was performed during the controlled experiment.
Provide the output of: Backlog Template; Module; GPIO 255
ESP-01S Relay: RESULT {NAME: ESP-01S Relay, ARCH: ESP8266, GPIO: [224,288,1,1,0,0,0,0,0,0,0,0,0,0], FLAG: 0, BASE: 18} RESULT {Module: {0: ESP-01S Relay}} RESULT {GPIO0: {Relay1: 224}, GPIO1: {Led1: 288}; remaining GPIOs: None}
Athom Plug V2: RESULT {NAME: Athom Plug V2, ARCH: ESP8266, GPIO: [0,0,0,3104,0,32,0,0,224,576,0,0,0,0], FLAG: 0, BASE: 18} RESULT {Module: {0: Athom Plug V2}} RESULT {GPIO3: {CSE7766 Rx: 3104}, GPIO5: {Button1: 32}, GPIO12: {Relay1: 224}, GPIO13: {LedLink_i: 576}; remaining GPIOs: None}
If using rules, provide: Backlog Rule1; Rule2; Rule3
On both representative devices, Rule1, Rule2, and Rule3 are OFF with empty Rules strings.
Provide the relevant output of: Status 0 (network identifiers redacted)
ESP-01S Relay: Version 15.5.0(release-tasmota); Hardware ESP8266EX; CpuFrequency 80; FlashSize 1024; WebLog 2; MqttLog 0; SysLog 0; KEEPALIVE 100; SOCKET_TIMEOUT 10.
Athom Plug V2: Version 15.5.0(release-tasmota); Hardware ESP8285H16; CpuFrequency 80; FlashSize 2048; WebLog 2; MqttLog 0; SysLog 0; KEEPALIVE 100; SOCKET_TIMEOUT 10; MqttCount 214; Wi-Fi LinkCount 1.
Athom Mini Relay V2 (bedroom AC power relay): Version 15.5.0(release-tasmota32); Hardware ESP32-C3 v0.4; CpuFrequency 160; FlashSize 4096; WebLog 2; MqttLog 0; FileLog 0; SysLog 0; KEEPALIVE 100; SOCKET_TIMEOUT 10; MqttCount 3.
Set weblog to 4, reproduce, and provide Console output (not performed deliberately: the production mitigation remains enabled, so reproducing the failure would require disabling a working protection).
No new intrusive reproduction has been performed after the successful mitigation. Existing broker-side evidence is summarized below. A targeted device-side log can be collected if maintainers require it.
TO REPRODUCE
The timing failure requires a network condition in which the broker-to-device TCP segment containing an MQTT PINGRESP is not promptly ACKed, while the Wi-Fi association stays up.
- Use an ESP8266 Tasmota device with a Mosquitto broker on Linux.
- Configure MqttKeepAlive 100.
- Allow the connection to remain mostly idle until the keepalive exchange occurs.
- When the initial TCP delivery of the 2-byte PINGRESP is not ACKed, observe normal Linux TCP exponential retransmission backoff.
- If recovery is delayed beyond the Tasmota/PubSubClient keepalive response window, Tasmota closes the MQTT connection before Linux TCP retransmission recovery is exhausted.
Broker-side packet and TCP socket observation established this sequence:
Tasmota -> Mosquitto: MQTT PINGREQ
Mosquitto -> Tasmota: MQTT PINGRESP (2 bytes)
Broker: no TCP ACK received for that data
Linux TCP: retransmission with exponential RTO backoff
Tasmota: closes MQTT connection
Mosquitto: connection closed by client
Tasmota: reconnects; MqttCount increasesThe retransmitted-data increase was 2 bytes, consistent with retransmission of PINGRESP.
EXPECTED BEHAVIOUR
A transiently lost or delayed PINGRESP should not make Tasmota terminate a TCP connection that still has a realistic opportunity to recover through normal TCP retransmission.
In particular, the timeout for awaiting PINGRESP should allow a reasonable transport-level recovery period and preferably be configurable independently of the MQTT keepalive interval.
SCREENSHOTS
Not applicable. This is a packet/timing behavior; no screenshot conveys the relevant evidence.
ADDITIONAL CONTEXT
Current source review
The current Tasmota development branch still limits MqttKeepAlive to 1..100 seconds. Its bundled TasmotaPubSub client checks an outstanding ping after the keepalive interval and changes state to MQTT_CONNECTION_TIMEOUT before stopping the client. This report concerns the policy interaction between that client timeout and a still-recovering Linux TCP connection.
Scope across hardware
The packet-level timing evidence and controlled pre/post experiment below were collected on two ESP8266 devices. The same historical MQTT reconnect symptom was also observed on the reporter's ESP32-C3 Athom Mini Relay V2 used as the bedroom AC power relay. This expands the observed hardware scope, but it is not claimed that the full PINGRESP timing sequence was separately captured on that ESP32-C3 device.
Controlled experiment
To separate the trigger (packet loss) from the timeout interaction, the following Linux TCP setting was enabled on the Mosquitto host:
net.ipv4.tcp_thin_linear_timeouts = 1This does not remove packet loss. It changes retransmission behavior for thin TCP streams so that retransmission timeouts do not immediately grow exponentially.
| Observation | ESP8266 device A | ESP8266 device B |
|---|---|---|
| Comparable window before the setting | 42 keepalive-related MQTT disconnects | 51 keepalive-related MQTT disconnects |
| 23 h 22 min after enabling the setting | 0 keepalive-related MQTT disconnects | 0 keepalive-related MQTT disconnects |
| TCP retransmissions during the successful test | about 848 | about 676 |
| Wi-Fi connect count | remained 1 | remained 1 |
The observation interval was passive: it sent no MQTT commands, HTTP requests, ICMP probes, Tasmota commands, or Home Assistant service calls.
After enabling the setting, packet loss/retransmissions continued frequently. However, retransmission RTO values were on the order of hundreds of milliseconds and no keepalive-related MQTT disconnect occurred. This supports the conclusion that packet loss alone is not what forces the disconnect; the disconnect occurs when application-layer timeout expires before transport recovery completes.
One separate reconnect followed a Mosquitto malformed packet error. It is a distinct failure mode and is excluded from the keepalive results.
Current workaround
The server-side workaround is effective but not a client-side fix:
net.ipv4.tcp_thin_linear_timeouts = 1It requires altering TCP behavior on the broker host. Tasmota's available MqttKeepAlive range is limited to 1-100 seconds, and there appears to be no separate user-configurable timeout for waiting for PINGRESP.
Possible fix direction
No particular implementation is requested. Possible approaches include:
- expose a separate configurable timeout for waiting for PINGRESP;
- allow it to be substantially longer than MqttKeepAlive;
- change the keepalive policy so one outstanding PINGRESP does not close the socket before reasonable transport recovery has had time to complete;
- or expand the permitted keepalive range, although separating the interval from the response timeout would be cleaner.
The essential requirement is that the application-layer failure timeout should not unnecessarily expire while TCP still has a realistic opportunity to recover a thin, mostly idle connection.
I can collect the template's requested device diagnostics and perform a targeted test on an up-to-date development build if that is required.
Source: arendst/Tasmota