Wireshark MQTT Debugging: The Same Capture in Mqttable
Troubleshoot MQTT disconnects, missing messages and QoS 1 duplicates with Wireshark filters and Mqttable, then resend a reviewed message with Replay.

Your MQTT client connects, then drops again. A subscription succeeds, but no data arrives. You publish once, yet the receiver gets the same message twice. Where do you start in a packet capture?
This tutorial follows those three problems. You will use Wireshark to find the relevant packets, then open the same capture in Mqttable to follow Client IDs, connections and issue details. After correcting the problem, you can use Replay to send the message again and check the receiver.
You need Wireshark, Mqttable and a .pcap or .pcapng file. The example uses mqtt-debug-lab.pcapng; when working with your own capture, replace its port, Client IDs and Topics with yours.
If you need a capture first, choose the interface carrying your MQTT traffic in Wireshark. On macOS, use lo0 for a local client talking to a local broker; for a remote broker, choose the Wi-Fi or Ethernet interface you are using. Set the capture filter to tcp port 1883, or your broker's port. Start capturing before you connect, subscribe and publish, then reproduce the problem, stop and save the file.
Open the file and enter this display filter:
mqttIf the Protocol column shows only TCP when you open the file, or mqtt matches nothing, check whether the port needs manual decoding. The example uses 38885. In Analyze → Decode As…, assign that TCP port to MQTT and apply the filter again.
Keep No., Time, Source, Destination and Info visible. Expand the MQTT details, right-click Client ID, Packet Identifier, QoS or DUP and choose Apply as Column to compare them in the packet list. The Wireshark MQTT filter reference lists the field names.
In Mqttable, choose PCAP → Open Capture…, import the same file and wait for Done. Overview lists the issues you can investigate in the following cases.

Check whether two clients use the same Client ID. For example, two service replicas named lab-dup can compete for the same MQTT session.
Enter this filter in Wireshark:
mqtt.msgtype == 1 && mqtt.clientid == "lab-dup"Expand the TCP details on matching CONNECT packets to read their stream numbers. The example has two CONNECT packets, frames 81 and 93, on streams 4 and 5. Filter both complete connections:
tcp.stream == 4 || tcp.stream == 5You can now inspect both complete connections. After the second CONNECT, the old connection receives DISCONNECT 0x8E in frame 95, with Session taken over in its details. This MQTT 5 reason, together with the two identical Client IDs, points you toward client identity configuration.
Open Issues → Overlapping client id connections. The issue groups the overlapping connections and shows lab-dup with both endpoints.

In Connections, select lab-dup. The table narrows to that client's two TCP connections. Select a Flow to inspect its packets and related issues.

Return to Issues, open Session takeover signal, choose Open decoded MQTT packets and select DISCONNECT. Message Details shows 0x8E - Session taken over.

In Wireshark, you associate CONNECT packets with streams before inspecting the disconnect. In Mqttable, you can follow the Client ID, both connections and the disconnect packet together. This saves you from maintaining that mapping by hand in a multi-client capture.
Check service replicas, startup arguments or Client ID generation so clients that must stay online together have distinct identities. Reconnect and check whether the old client is still displaced. The MQTT 5 specification describes this reason code.
Before changing subscription QoS, check whether the broker accepted the publish. Connection and subscription success do not guarantee publish permission.
The example receiver subscribes to lab/# and receives other Topics, but nothing arrives on lab/denied/telemetry. The publisher also connects successfully, so inspect the response following PUBLISH.
Find that Topic's publish in Wireshark:
mqtt.msgtype == 3 && mqtt.topic == "lab/denied/telemetry"Frame 123 is a QoS 1 publish with Packet Identifier 1. Follow the same TCP stream to its PUBACK: frame 125 returns 0x87 / Not authorized. You can also filter these rejections directly:
mqtt.msgtype == 4 && mqtt.puback.reason_code == 0x87Open Issues → QoS acknowledgement error reason to see Not authorized (135). Follow the packet link to inspect the corresponding PUBACK.

Check whether the publishing account has write permission for the Topic and whether the application uses the intended Topic. The example permits writes under lab/allowed/#; changing the publish path to lab/allowed/telemetry restores receipt.
0x87 is a reason code carried by MQTT 5 PUBACK. MQTT 3.1.1 PUBACK has no such field, so also inspect broker logs when checking publish permission.
When you retest, check both the successful PUBACK at the publisher and the actual message at the receiver. In this example, mosquitto_pub printed the rejection but exited with code 0, so checking its exit status alone would miss the problem.
The publisher calls publish once, but the receive log contains two copies of:
{ "case": "ack-loss", "seq": 1 }Look for MQTT retransmission first. Filter PUBLISH packets with DUP set:
mqtt.msgtype == 3 && mqtt.dupflag == 1Frame 173 matches. It has the same Packet Identifier 1, Topic and payload as the original publish in frame 139. The original has DUP=False; the retransmission has DUP=True.
Inspect both publisher connections together:
tcp.stream == 7 || tcp.stream == 9The publisher did not receive PUBACK after its first publish. It disconnected, reconnected with the persistent session and retransmitted the unacknowledged message before receiving PUBACK. The receiver had already received the first copy, so the retransmission produced another receipt.
This case uses an MQTT 3.1.1 persistent session. Its retry rules explain what happens to unacknowledged packets on reconnect; elapsed time alone does not mean the client must resend.
Open Issues → QoS 1 PUBLISH without PUBACK. The detail points to the original publish and asks you to inspect its acknowledgment path.

Choose Open decoded MQTT packets, inspect PUBLISH and responses on that connection, then examine the connection after reconnect. The missing response is MQTT PUBACK: TCP ACK is not a substitute, and Wireshark's tcp.analysis.retransmission does not directly identify MQTT retransmission.
For your own capture, also check that both directions were collected and recording did not stop early. This example blocks PUBACK in a relay; a capture that merely omitted the response would need a different investigation.
Restore the acknowledgment path, and make the receiving application recognize duplicate business IDs. DUP is a protocol clue; check application logs to find out whether the same business operation was processed twice.
After changing Client IDs, Topic permissions or network rules, reconnect, publish and save a new capture. Import it into Mqttable and check whether the original issues still appear.

Some checks may show Not enough evidence or No traffic. The first means the file does not support a decision; the second means the relevant traffic was not captured. Perform the operation you need to check rather than treating either state as a pass.
Open a relevant check in Checks to inspect its explanation and related packets. Free shows the top five diagnostic details; summary, issue and packet views remain available.

To retest with the same Topic and payload, prepare the message as a TraceGrid CSV and import it into Replay. Export with Packets → Export Trace, keep the header and all columns, and select only the original outbound PUBLISH. Leave out its retransmission and the receiver's copies.
The example uses replay-reviewed-single-publish.csv, containing only the original lab-qos1-publisher publish at 15:35:42.562255+08:00, corresponding to Wireshark frame 139.
- Remove the rule blocking PUBACK and leave the receiver subscribed.
- Save a broker and create a client in Connections, then import the CSV in Replay and select your retest broker. The example target is Wireshark MQTT Lab at
mqtt://127.0.0.1:38885. - Choose Dry Run and review the target, Topic, message count and client identity. This example plans one publish with an isolated Replay Client ID to avoid competing with the original client. Review credential fallback or other attention items against your target configuration before continuing.

- Choose Run Replay. In Run Trace, check that PUBLISH received a successful PUBACK, then confirm the same message arrived at the receiver.

The example Run Trace shows the publish completed and the receiver got the message once. Replay repeats MQTT client actions; identity collisions, network faults and business processing still need checks appropriate to your test. To automate received-message checks, continue with Replay Expected results.
Scroll the table sideways to see all columns.
| What you need to do | Where to start |
|---|---|
| Inspect TCP retransmission, windows, connection closure or other protocols | Wireshark for lower-level communication details |
| Find the MQTT connections associated with a Client ID | Mqttable Connections, scoped to that client |
| Find packets behind a disconnect, rejection or missing acknowledgment | Mqttable Issues, then Packets |
| Understand why an MQTT check needs attention | Mqttable Checks, with its explanation and packets |
| Verify a correction by sending the same message again | Replay with a reviewed CSV, plus the receiver's result |
Wireshark is useful for detailed inspection of raw network traffic. Mqttable keeps MQTT clients, connections, issue details and packets together, with a path to a rerun afterward. Use Wireshark to inspect the underlying communication and Mqttable to follow the MQTT connection and message workflow.
If mqtt matches nothing, check the interface, port and Decode As settings. With TLS, MQTT content is encrypted; assigning a port to MQTT does not decrypt it. You need a plaintext observation point or decryption material you are authorized to use.
If CONNECT appears but later PUBLISH packets do not, stop filtering only by mqtt.clientid. PUBLISH usually has no Client ID. Find the TCP stream from CONNECT and inspect that connection instead.
Before sharing a capture, check for sensitive passwords, Topics and payloads. Uploading a file to Remote Runtime sends it to that Runtime; hiding a password in the interface does not sanitize the source file.