ROS 2 QoS Compatibility: Why Your Publisher and Subscriber Refuse to Talk
In ROS 2, a matching topic name and message type are not enough for two nodes to communicate. The publisher's offered Quality of Service (QoS) profile must satisfy the subscription's requested profile, policy by policy. If any requested policy is stricter than the one on offer, the middleware makes no connection and passes no data. It also raises no exception. The usual culprits are reliability (a best-effort publisher with a reliable subscriber) and durability (a volatile publisher with a transient-local subscriber).
This guide is based on the ROS 2 Lyrical Luth documentation (the long-term support release, published on 22 May 2026 and supported until May 2031). Whether you are writing a final-year robotics project at university or commissioning a robot cell for a manufacturing client, the behaviour is the same. The rules and commands quoted here can be checked against the official pages in the ROS 2 documentation base.
What “incompatible QoS” actually means
A QoS profile is a set of policies: history, depth, reliability, durability, deadline, lifespan, liveliness and lease duration. You can set a profile independently on every publisher, subscription, service server and client. The documentation warns that when different profiles are used, they may be incompatible, which prevents messages from being delivered.
Compatibility follows a Request vs Offered model. The subscription states the minimum quality it will accept, and the publisher states the maximum it can provide. A connection is made only if every requested policy is no more stringent than the offered one. Two details often surprise people:
- One publisher can serve several subscriptions with different requested profiles simultaneously.
- A mismatch on a single policy is enough to block the pair, even if every other policy lines up.
A worked example: a camera on a lossy link
The documentation includes a demo that maps neatly onto a common lab situation, such as a camera streaming over flaky Wi-Fi to an operator's laptop. ros2 run image_tools cam2image publishes images, and ros2 run image_tools showimage displays them. On Linux, sudo tc qdisc add dev lo root netem loss 5% simulates 5% packet loss on the loopback device.
With the default settings, both programs appear to slow down. Reliable delivery means the publisher resends packets until the consumer acknowledges them. Re-running both with -p reliability:=best_effort lets frames drop instead, so the frame numbers stop being consecutive but the stream keeps up. Afterwards, remove the rule with sudo tc qdisc delete dev lo root netem loss 5%. The docs note that this part of the demo does not work with Connext DDS or Fast DDS on a single host, because they use shared memory, which the loopback throttle does not affect.
Now suppose a colleague adds a recording node that keeps the default profile, which requests reliable. If the camera publisher has been switched to best effort, that recorder never receives a frame. The camera, the viewer and the recorder all look healthy, yet one of them sees nothing.
The two policies behind most broken connections
Reliability and durability pairings, as listed in the ROS 2 Lyrical documentation
| Publisher offers | Subscriber requests | Outcome |
|---|---|---|
| Best effort | Reliable | No connection |
| Reliable | Best effort | Connects |
| Best effort or Reliable | Same as publisher | Connects |
| Volatile | Transient local | No communication |
| Transient local | Volatile | Connects, new messages only |
| Transient local | Transient local | Connects, new and old messages |
| Volatile | Volatile | Connects, new messages only |
The policy definitions explain the ordering. Best effort attempts delivery but may lose samples on a poor network. Reliable guarantees delivery and may retry several times. Transient local makes the publisher responsible for persisting samples for late-joining subscriptions. Volatile makes no attempt to persist them.
Deadline, liveliness and lease duration can also block a connection. An Automatic liveliness publisher will not satisfy a Manual by topic subscription. A publisher left on the Default deadline or lease duration will not satisfy a subscription that asks for a specific duration.
Coming from ROS 1: TCPROS, UDPROS and latching
If your team still maintains ROS 1 code, the documentation's comparison helps. ROS 2 reliability corresponds to TCPROS (the ROS 1 default) for reliable and to UDPROS (roscpp only) for best effort. Even reliable ROS 2 traffic runs over UDP, which allows multicast where appropriate. History plus depth replaces the ROS 1 queue size.
Latching becomes durability. Transient local with any depth behaves like a latched publisher, but both ends must use transient local for a late subscriber to receive the last message. In ROS 1, any publisher and subscriber with the same type on the same topic were connected. ROS 2 adds the possibility of incompatible profiles, and the docs flag this as something new to be aware of.
A five-step diagnostic checklist
- Run ros2 doctor --report. Its QOS COMPATIBILITY LIST names the topic, both nodes and the problem, for example ERROR: Best effort publisher and reliable subscription.
- Open rqt_graph, which can also detect and report QoS incompatibilities between publishers and subscriptions.
- Compare the endpoints with ros2 topic info <topic> --verbose. Each publisher and subscription is listed with its Reliability, History (Depth), Durability, Lifespan, Deadline and Liveliness.
- For request/response traffic, use ros2 service info --verbose, which is new in Lyrical and prints the QoS profiles of the request reader and response writer. The same compatibility rules apply to services.
- In code, register callbacks for the Offered incompatible QoS (publisher side) and Requested incompatible QoS (subscription side) events, or use matched events to log every connection made or dropped.
Do not forget the domain
If no endpoint appears at all, QoS is not the problem. Nodes only discover each other within the same ROS_DOMAIN_ID (0 by default), and the docs suggest choosing a value between 0 and 101 inclusive.
Recording with rosbag2 without missing topics
Recording is where mismatches often surface first. According to the rosbag2 guide, only reliability and durability determine compatibility. Ros2Bag adapts its profile when recording and playing back, and also tries to preserve the policy originally offered during playback. When that is not enough, write a YAML override and pass it with --qos-profile-overrides-path. The documented example records a transient local /talker topic with ros2 bag record -a -o my_bag --qos-profile-overrides-path durability_override.yaml, and plays it back as best effort with ros2 bag play --qos-profile-overrides-path reliability_override.yaml my_bag.
When Zenoh changes the picture
The strict matching above belongs to DDS-based middleware. Fast DDS is Lyrical's default, and Cyclone DDS and Connext are alternatives. The documentation describes Zenoh as a lighter-weight alternative that keeps QoS features, with essentially no incompatible QoS settings. It is aimed at IoT and edge scenarios where throughput, latency and interoperability across mixed environments matter most. rmw_zenoh_cpp has been Tier 1 since Kilted Kaiju.
Switching is done per terminal with export RMW_IMPLEMENTATION=rmw_zenoh_cpp, plus a running router (ros2 run rmw_zenoh_cpp rmw_zenohd). Without the router, nodes cannot discover each other. The documentation also recommends that every part of a distributed system uses the same ROS version and the same RMW implementation.
Check a QoS rule against the official text
Kopik's ROS 2 base indexes the Lyrical Luth documentation, including the concepts, tutorials, how-to guides and release notes, and answers with the passages it relied on. Try “I want a topic that behaves like a latched topic in ROS 1. Which QoS setting do I need on both ends?”
Other expert bases are listed in the catalogue. The ROS 2 base is limited to the core documentation, so it does not cover Nav2 or MoveIt 2 configuration.
Frequently asked questions
Why do ROS 2 nodes on the same topic not communicate?
If both endpoints are visible, their QoS profiles are probably incompatible: a requested policy is stricter than the offered one. If no endpoint is visible at all, check that both nodes use the same ROS_DOMAIN_ID and the same RMW implementation.
Which ROS 2 QoS policies affect compatibility?
The compatibility tables cover reliability, durability, deadline, liveliness and lease duration. For rosbag2 recording, the documentation notes that only reliability and durability decide whether publishers and subscribers can exchange data.
What does the sensor data QoS profile use?
The documentation says the sensor data profile uses best effort reliability and a smaller queue size, because timely readings matter more than receiving every one. A subscription that requests reliable will therefore not connect to a publisher using this profile.
Can I change QoS from the command line for testing?
Yes. ros2 topic pub supports options such as --qos-reliability, --qos-durability and --qos-profile, and ros2 topic echo accepts --qos-reliability best_effort, as shown in the rosbag2 QoS override guide.
Do services have QoS compatibility rules too?
Yes. The documentation states that the compatibility rules apply to service servers and clients in the same way as to publishers and subscriptions. In Lyrical, ros2 service info --verbose shows the profiles involved.
Get the Kopik newsletter
New knowledge bases, RAG guides and product news. One email every week or two, unsubscribe in one click.
By subscribing you agree to receive our newsletter. We never share your address.