Skip to content

[BUG] Duplicate component created for root namespace nodes due to topic-based discovery collision #146

@mfaferek93

Description

@mfaferek93

Bug report

Please use this template to report bugs.

Steps to reproduce

  1. Run any demo/system with a node in root namespace (e.g., namespace="") that publishes topics under its node name (e.g., /fault_manager/events)
  2. Use gateway in runtime_only mode with create_synthetic_components: true and topic_only_policy: create_component
  3. Check /api/v1/components endpoint

The node should appear only once - as part of the synthetic "root" component (or as a single component if grouping is disabled).

Actual behavior

The node appears twice:

  1. As an app inside synthetic component "root" (correct)
  2. As a separate topic-based component with ID matching the node name (e.g., "fault_manager")

Root cause: In runtime_discovery.cpp, get_node_namespaces() (line 478) excludes "root" area from the namespace set:

if (area != "root") {
namespaces.insert(area);
}

This means node names in root namespace (like "fault_manager") are not added to node_namespaces. When discover_topic_components() processes topic /fault_manager/events, it extracts namespace "fault_manager" and checks if it's in node_namespaces. Since it's not there (only areas like "sensors", "processing" are), a duplicate topic-based component is created.

Trace:
get_node_namespaces() returns: {"sensors", "processing", "bridge", "diagnostics"}
(missing: "fault_manager" because area="root" is skipped)

discover_topic_components() for topic /fault_manager/events:
→ ns = "fault_manager"
→ node_namespaces.count("fault_manager") = 0 ← NOT FOUND
→ Creates topic-based component "fault_manager" ← DUPLICATE!

Environment

  • ros2_medkit version: main branch
  • ROS 2 distro:
  • OS:

Additional information

Affected file: src/ros2_medkit_gateway/src/discovery/runtime_discovery.cpp

Proposed fix: In get_node_namespaces(), also add node names for nodes in root namespace:

std::set<std::string> RuntimeDiscoveryStrategy::get_node_namespaces() {                                                                                                                    
  std::set<std::string> namespaces;                                                                                                                                                        
                                                                                                                                                                                             
  auto node_graph = node_->get_node_graph_interface();                                                                                                                                     
  auto names_and_namespaces = node_graph->get_node_names_and_namespaces();                                                                                                                 
                                                                                                                                                                                             
  for (const auto & name_and_ns : names_and_namespaces) {                                                                                                                                  
    std::string ns = name_and_ns.second;                                                                                                                                                   
    std::string name = name_and_ns.first;                                                                                                                                                  
    std::string area = extract_area_from_namespace(ns);                                                                                                                                    
                                                                                                                                                                                             
    if (area != "root") {                                                                                                                                                                  
      namespaces.insert(area);                                                                                                                                                             
    } else {                                                                                                                                                                               
      // For root namespace nodes, add node name to prevent                                                                                                                                
      // topic-based component collision with /node_name/* topics                                                                                                                          
      namespaces.insert(name);                                                                                                                                                             
    }                                                                                                                                                                                      
  }                                                                                                                                                                                        
                                                                                                                                                                                             
  return namespaces;                                                                                                                                                                       
}                                                                                                                                                                                          

Discovered in: sensor_diagnostics demo where fault_manager runs in root namespace.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions