Skip to content

rcl_yaml_param_parser treats bare nan/inf as doubles via fallback (inconsistent with composable node parameter loading) #1320

Description

@jayyoung

Operating System:

Linux 6.8.0-111-generic #111-Ubuntu

ROS version or commit hash:

Kilted

RMW implementation (if applicable):

rmw_cyclonedds_cpp

Steps to reproduce issue

We produced a minimal working example you can pull and run to see the difference in parsing behaviour between composed/standalone nodes here: https://github.com/botsandus/yaml_nan_type_bug

The bare basics of what the above is doing is as follows:

  1. We ceate a parameter yaml containing a bare nan value:
node_name:
  ros__parameters:
    some_param: nan
  1. We create a node that declares a parameter and reads it as a double:
node->declare_parameter("some_param", 0.0,
    rcl_interfaces::msg::ParameterDescriptor().set__dynamic_typing(true));
auto val = node->get_parameter("some_param").as_double();
  1. Launch it as a standalone node with --params-file:
ros2 launch yaml_nan_type_bug standalone.launch.py

The node starts successfully. The parameter is loaded as a double (NaN).

  1. Launch the same node as a composable node via LoadComposableNodes into a component_container:
ros2 launch yaml_nan_type_bug composed.launch.py

The node throws InvalidParameterTypeException: parameter 'some_param' has invalid type: expected [double] got [string] and the container crashes.

Expected behavior

We would expect that both launch methods should produce the same parameter type for the same yaml input. The nan/inf should be treated consistently, either as a double in both paths or as a string in both paths.

We discovered that in the yaml 1.1 spec, only dot-prefixed forms (like .nan, .NaN, .NAN, .inf, .Inf, .INF) are valid float literals, and bare nan is not a valid yaml float.

Actual behavior

  • Standalone node: rcl_yaml_param_parser parses bare nan as a double via strtod() fallback
  • Composable node: launch_ros uses yaml.safe_load() (part of pyyaml), which correctly treats bare nan as a string per the yaml 1.1 spec.

This means migrating a node from standalone to composable can silently change parameter types, causing crashes or behavioural differences. This is how we discovered the issue originally 🙀

Additional information

Root cause: In rcl_yaml_param_parser/src/parse.c, the float detecton in get_value() explicitly checks for yaml 1.1 dot-prefixed float forms, then falls through to strtod() for all other values. This makes the C parser accidentally more permissive than we might expect from the spec

How the two paths are different: Standalone nodes load parameters via --params-file, parsed by the C-level rcl_yaml_param_parser within the node process. Composable nodes can't do this because the container process is already running, so instead, launch_ros reads the yaml using yaml.safe_load(), and serialises the the values into the composition_interfaces/srv/LoadNode service request, and sends to the container. These two parsers disagree on whether bare nan/inf are floats or strings.

We investigated a bit more on this, and we do have a proposed fix, but I wanted to open this issue first so there could be some feedback, but what we think we can do is this:

Proposed fix: Reject strtod() results that are nan or inf, since these can only come from bare nan/inf (the valid dot-prefixed forms are already handled above). Approximately 2 lines added around here in rcl_yaml_param_parser we could do this:

dval = strtod(value, &endptr);
if (endptr != value && (isnan(dval) || isinf(dval))) {
    endptr = (char *)value;
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions