Skip to content

TopicList() may return incomplete topic information #914

Description

@C88-YQ

Environment

  • OS Version: Ubuntu 24.04.4 LTS
  • Source or binary build?
    • Source build
    • Branch: main
    • gz-transport version: latest commit
  • ROS 2 Version: Rolling
  • Gazebo Sim Version: v11.0.0~pre1

Description

  • Expected behavior:
    gz::transport::Node::TopicList() should return all available Gazebo Transport topics after the simulation has started, including topics that only have subscribers.

  • Actual behavior:
    gz::transport::Node::TopicList() always returns an incomplete topic list.

Steps to reproduce

Setup

A minimal reproduction package is provided here: https://github.com/azeey/gsoc2026_multirobot/tree/main/bridge/topiclist_test

The package contains a simple ROS 2 node (topiclist_test_node) that:

  • Creates a Gazebo Transport node.
  • Periodically calls gz::transport::Node::TopicList().
  • Compares the returned topic list with a predefined list of expected Gazebo topics.
  • Reports missing topics in the console output.

The node supports the following launch parameters:

  • use_composition: whether to load the node as a composable node.
    • Default: True
  • freq: the frequency (Hz) at which TopicList() is called.
    • Default: 1.0

Steps

  1. Build & Source
colcon build --symlink-install
source install/setup.bash
  1. Launch
ros2 launch topiclist_test topiclist_test.launch.py use_composition:=True freq:=1.0
  1. Observe the output
    The node periodically prints the number of expected topics, the number of topics returned by TopicList(), and any missing topics.

Output

The test was performed with different TopicList() query frequencies (0.1 Hz, 1 Hz, and 2 Hz) and with both composable and non-composable node modes.

In all cases, the same incomplete topic list was consistently returned. The node was running for more than 2 minutes, but the missing topics did not appear in the result.

[INFO] [topiclist_test_node]: Expected Gazebo topics: 25, discovered by TopicList(): 21

[INFO] [topiclist_test_node]: Missing topics:
  /model/vehicle/cmd_vel
  /model/vehicle/enable
  /world/test_world/light_config
  /world/test_world/material_color

Additional information

1. Possible cause: asynchronous discovery

The issue appears to be related to the asynchronous nature of Gazebo Transport discovery.

From the current implementation, TopicList() requests subscriber information and then immediately reads the cached discovery information. Since discovery responses are processed asynchronously, the cache may not contain all remote subscriber information when the topic list is generated.

2. Comparison with gz topic --list

Interestingly, the command line tool gz topic --list does not show this issue. Under the same simulation setup, it returns the complete topic list, including the topics missing from TopicList().

This suggests that the underlying discovery mechanism can eventually obtain the complete topic information, but TopicList() may return before all discovery responses have been received.

3. Experiment: waiting for discovery responses

Adding a small delay inside TopicList() before reading the discovery cache improves the completeness of the result. For example, adding a sleep after requesting subscriber information in [Discovery::TopicList()] (

public: void TopicList(std::vector<std::string> &_topics)
{
if (!this->useZenoh)
{
std::lock_guard<std::mutex> lock(this->mutex);
this->remoteSubscribers.Clear();
}
// Request the list of subscribers.
Publisher pub("", "", this->pUuid, "", AdvertiseOptions());
this->SendMsg(
DestinationType::ALL, msgs::Discovery::SUBSCRIBERS_REQ, pub);
this->WaitForInit();
std::lock_guard<std::mutex> lock(this->mutex);
this->info.TopicList(_topics);
std::vector<std::string> remoteSubs;
this->remoteSubscribers.TopicList(remoteSubs);
// Add the remote subscribers
for (auto const &t : remoteSubs)
{
if (std::find(_topics.begin(), _topics.end(), t) == _topics.end())
{
_topics.push_back(t);
}
}
}
):

      public: void TopicList(std::vector<std::string> &_topics)
      {
        ...
        // Request the list of subscribers.
        Publisher pub("", "", this->pUuid, "", AdvertiseOptions());
        this->SendMsg(
          DestinationType::ALL, msgs::Discovery::SUBSCRIBERS_REQ, pub);

        this->WaitForInit();

        std::this_thread::sleep_for(std::chrono::milliseconds(1));

        std::lock_guard<std::mutex> lock(this->mutex);
        this->info.TopicList(_topics);
        ...
    }

With this modification, TopicList() returns the complete topic list in the current reproduction case.

However, the required waiting time depends on the number of topics and the discovery state. In a scenario with a larger number of topics, a longer delay is required before TopicList() can return the complete topic list.

4. Local process discovery limitation

Additionally, another issue was observed when using use_composition:=True. The discovery logic ignores information from local processes, so topics subscribed by nodes running in the same process are not included in the result. Therefore, when the test node is loaded as a composable node in the same process, TopicList() cannot discover topics from that local process.

gz-transport/src/Discovery.hh

Lines 1123 to 1142 in 0fdc2df

private: void DispatchDiscoveryMsg(const std::string &_fromIp,
char *_msg, uint16_t _len)
{
gz::msgs::Discovery msg;
// Parse the message, and return if parsing failed. Parsing could
// fail when another discovery node is publishing messages using an
// older (or newer) format.
if (!msg.ParseFromArray(_msg, _len))
return;
// Discard the message if the wire protocol is different than mine.
if (this->Version() != msg.version())
return;
std::string recvPUuid = msg.process_uuid();
// Discard our own discovery messages.
if (recvPUuid == this->pUuid)
return;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Inbox

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions