Your ROS2 Subscriber Is Not Receiving Messages. No Error. No Warning. Nothing.
Your publisher is running. `ros2 topic echo` shows data flowing out. Your subscriber node is alive. The callback never fires. No error in the log. No warning on the console. Nothing. This is a QoS mis

Blai Morte
Co-Founder
Your publisher is running. ros2 topic echo shows data flowing out. Your subscriber node is alive. The callback never fires. No error in the log. No warning on the console. Nothing. This is a QoS mismatch, and ROS2 chose not to tell you about it.
What QoS Actually Controls
QoS (Quality of Service) in ROS2 is not a suggestion. It is a contract between a publisher and a subscriber. The contract has seven dimensions: reliability, durability, history, depth, deadline, lifespan, and liveliness. A mismatch on any dimension means the connection is silently dropped.
The most common mismatch is reliability. A publisher using RELIABLE and a subscriber using BEST_EFFORT will not talk to each other. The topic exists. Both sides are running. The DDS layer discards the connection and moves on.
This is not a networking bug. This is a design decision in ROS2: the runtime knows both sides of the connection, knows they are incompatible, and reports nothing by default.
The Seven Dimensions That Can Break You
Reliability is the one most people hit first. But the others matter too.
Durability — VOLATILE vs TRANSIENT_LOCAL. A VOLATILE publisher does not retain messages for subscribers that connect late. A TRANSIENT_LOCAL subscriber expects the last N messages on connect. If they do not match, the subscriber waits for data that will never arrive.
History — KEEP_LAST vs KEEP_ALL. KEEP_LAST with depth 10 keeps the 10 most recent messages. KEEP_ALL tries to keep everything. On a fast publisher with a slow subscriber, KEEP_ALL on both sides can cause memory growth and dropped messages without warning.
Depth — Works with KEEP_LAST. A depth of 1 means the subscriber only buffers the latest message. If your callback processing takes longer than the publishing interval, you lose intermediate messages silently.
Deadline — Both sides agree on a maximum interval between messages. Miss the deadline and the DDS layer may report a missed deadline event, but ROS2 does not surface this as a topic-level error by default.
Lifespan — A publisher can set an expiration time on messages. If the subscriber is not ready to receive before the lifespan expires, the message is dropped. Again, silently.
You do not need to understand all seven to write a working ROS2 system. You do need to understand them to debug one that stopped working for no visible reason.
How to Find a QoS Mismatch
The tool exists. Most people learn about it after spending an hour eliminating every other possibility.
ros2 topic info /your_topic --verboseThis shows the QoS profile of every publisher and every subscriber on that topic. If reliability does not match, the connection is incompatible. The output does not flag this in red. It just lists the profiles side by side and leaves the inference to you.
ros2 topic info /cmd_vel --verboseLook for the Reliability line under Publishers and Subscribers. If one says RELIABLE and the other says BEST_EFFORT, that is your silent failure.
Fixing It in Code
When you create a publisher or subscriber, you can pass an explicit QoS profile. The defaults are RELIABLE for publishers and BEST_EFFORT for some subscribers, depending on the client library and the topic type. Do not assume they match.
from rclpy.qos import QoSProfile, ReliabilityPolicy, DurabilityPolicy
# Explicit profile for a control command
control_qos = QoSProfile(
reliability=ReliabilityPolicy.RELIABLE,
durability=DurabilityPolicy.VOLATILE,
depth=10
)
self.cmd_pub = self.create_publisher(Twist, '/cmd_vel', control_qos)# Matching subscriber
self.cmd_sub = self.create_subscription(
Twist,
'/cmd_vel',
self.cmd_callback,
control_qos # must match the publisher
)If the publisher and subscriber do not agree on reliability, the subscription will not connect. ros2 topic info will show both endpoints, but no data will flow.
A Common Case: Sensor Data on WiFi
The default RELIABLE setting on a wired local network works fine. On WiFi, especially at range or with interference, RELIABLE triggers constant retransmissions. DDS tries to guarantee delivery over a lossy medium, creates congestion, and your effective bandwidth collapses.
For sensor data — LIDAR scans, camera frames, IMU streams — switch to BEST_EFFORT:
sensor_qos = QoSProfile(
reliability=ReliabilityPolicy.BEST_EFFORT,
depth=10
)
self.scan_sub = self.create_subscription(
LaserScan,
'/scan',
self.scan_callback,
sensor_qos
)Stale sensor frames are discarded anyway by downstream processing. The retransmission overhead of RELIABLE on WiFi buys you nothing and costs latency and throughput.
For control commands — /cmd_vel, service calls, lifecycle transitions — keep RELIABLE. Missing a control command matters. Missing a single scan frame at 10Hz does not.
The Design Decision Worth Questioning
This failure mode is not hard to detect. The runtime compares both QoS profiles at connection time. It could log a warning. It could reject the subscription with an explicit error. It does neither.
The result: hours of debugging that start with "is the node running?" and end with "the reliability policy was wrong." The fix takes two minutes. The diagnosis takes two hours because the system chose not to surface the mismatch.
What to Check Before You Assume a Network Bug
When a subscriber stops receiving data, check these three things before you touch your network configuration:
1. Is the topic actually publishing? ros2 topic hz /your_topic 2. Do the QoS profiles match? ros2 topic info /your_topic --verbose 3. Is the subscriber alive? ros2 node info /your_node_name
If the topic is publishing and the node is alive but no data arrives, the QoS mismatch is the most likely cause. It is also the last place most people look.
Continue reading
Related Posts
Python ROS2 Nodes Start Fine. Then the Callbacks Start Drifting.
Most ROS2 teams start in Python. The API is cleaner. Iteration is faster. rclpy handles the majority of nodes without issue. Then at some point a timer fires late, a high-frequency publisher drops mes
May 26, 2026
ROS2 on Mac or Windows Means Docker. That Sentence Costs More Than Most Teams Realize.
ROS2 is Linux-native. That is not a complaint about the design choice — Linux is the right foundation for production robotics. The complaint is about what the development toolchain assumes: that every
May 26, 2026
10 Lines in ROS1. 50 in ROS2. The Launch File Problem Nobody Warned You About.
A developer on Reddit described migrating a real project from ROS1 to ROS2. The launch file went from 10 lines to 50. Same nodes. Same topics. Same robot. That is a real measurement from a real migrat
May 25, 2026