Before proceeding, is there an existing issue or discussion for this?
OS and version
Ubuntu 24.04
Open-RMF installation type
Binaries
Other Open-RMF installation methods
No response
Open-RMF version or commit hash
2.7.2-1noble.20260615.164301
ROS distribution
Jazzy
ROS installation type
Binaries
Other ROS installation methods
No response
Package or library, if applicable
No response
Description of the bug
In rmf_fleet_adapter/src/rmf_fleet_adapter/TaskManager.cpp, TaskManager::publish_task_state() builds up the phases JSON object using string keys everywhere except one block:
auto& phases = _state_msg["phases"];
...
auto& phase_state = phases[std::to_string(id)]; // every other phase entry: string key
but the skip-requests block a few lines later indexes the same phases object with the raw numeric map key instead:
for (const auto& [phase, skip_info] : _skip_info_map)
{
auto& skip_requests = phases[phase]["skip_requests"]; // BUG: phase is uint64_t
for (const auto& s : {&skip_info.active_skips, &skip_info.removed_skips})
for (const auto& [token, msg] : *s)
skip_requests[token] = msg;
}
phase is a uint64_t (flows from _active_task.skip(request_json["phase_id"].get<uint64_t>(), ...), which creates the entry via _skip_info_map[phase_id]). Since phases is already a JSON object by the time this runs (populated above via std::to_string(id) insertions), calling operator[] on it with an integral argument is exactly what nlohmann::json disallows. The mismatch is deterministic and unconditional — it fires on any valid phase_id, the moment a phase has ever had a skip recorded against it, on the very next state publish.
Related: open-rmf/rmf#374 (original, less precise report of the same crash — root cause not pinned to a line there).
Steps to reproduce the bug
- Dispatch any
compose/patrol task with 2+ phases.
- While it's active, send a
skip_phase_request for any existing phase — either via POST /tasks/skip_phase on the api-server, or directly over ROS on /task_api_requests:
{"type":"skip_phase_request","task_id":"<task_id>","phase_id":1}
- Observe the
fleet_adapter process.
Confirmed on two independent setups on ROS 2 Jazzy (ros-jazzy-rmf-fleet-adapter 2.7.2-1noble.20260615.165559):
- A custom
EasyFullControl-based fleet adapter with per-robot REST backends.
- A completely stock
rmf_demos_gz office demo (tinyRobot fleet, unmodified rmf_demos_fleet_adapter), ruling out any fleet-specific customization.
Also confirmed the same code path is present, unmodified, on the lyrical branch — so this isn't a jazzy-only regression.
Expected behavior
The skip request is recorded and the next publish_task_state() call successfully includes the skip_requests for that phase in the published task state, with the fleet adapter continuing to run normally.
Actual behavior
The fleet_adapter process crashes outright:
terminate called after throwing an instance of 'nlohmann::detail::type_error'
what(): [json.exception.type_error.305] cannot use operator[] with a numeric argument with object
This throws inside a C++ callback with no Python-side try/except to catch it (the fleet adapter is typically driven via rmf_fleet_adapter_python), so it's an uncaught exception → std::terminate → the entire process SIGABRTs (exit code -6), taking every robot in that fleet offline until the process is relaunched.
Additional information or screenshots
Fix — matches the key convention already used for every other entry in phases within the same function:
- auto& skip_requests = phases[phase]["skip_requests"];
+ auto& skip_requests = phases[std::to_string(phase)]["skip_requests"];
Before proceeding, is there an existing issue or discussion for this?
OS and version
Ubuntu 24.04
Open-RMF installation type
Binaries
Other Open-RMF installation methods
No response
Open-RMF version or commit hash
2.7.2-1noble.20260615.164301
ROS distribution
Jazzy
ROS installation type
Binaries
Other ROS installation methods
No response
Package or library, if applicable
No response
Description of the bug
In
rmf_fleet_adapter/src/rmf_fleet_adapter/TaskManager.cpp,TaskManager::publish_task_state()builds up thephasesJSON object using string keys everywhere except one block:but the skip-requests block a few lines later indexes the same
phasesobject with the raw numeric map key instead:phaseis auint64_t(flows from_active_task.skip(request_json["phase_id"].get<uint64_t>(), ...), which creates the entry via_skip_info_map[phase_id]). Sincephasesis already a JSON object by the time this runs (populated above viastd::to_string(id)insertions), callingoperator[]on it with an integral argument is exactly whatnlohmann::jsondisallows. The mismatch is deterministic and unconditional — it fires on any validphase_id, the moment a phase has ever had a skip recorded against it, on the very next state publish.Related: open-rmf/rmf#374 (original, less precise report of the same crash — root cause not pinned to a line there).
Steps to reproduce the bug
compose/patroltask with 2+ phases.skip_phase_requestfor any existing phase — either viaPOST /tasks/skip_phaseon the api-server, or directly over ROS on/task_api_requests:{"type":"skip_phase_request","task_id":"<task_id>","phase_id":1}fleet_adapterprocess.Confirmed on two independent setups on ROS 2 Jazzy (
ros-jazzy-rmf-fleet-adapter 2.7.2-1noble.20260615.165559):EasyFullControl-based fleet adapter with per-robot REST backends.rmf_demos_gzoffice demo (tinyRobotfleet, unmodifiedrmf_demos_fleet_adapter), ruling out any fleet-specific customization.Also confirmed the same code path is present, unmodified, on the
lyricalbranch — so this isn't a jazzy-only regression.Expected behavior
The skip request is recorded and the next
publish_task_state()call successfully includes theskip_requestsfor that phase in the published task state, with the fleet adapter continuing to run normally.Actual behavior
The
fleet_adapterprocess crashes outright:This throws inside a C++ callback with no Python-side try/except to catch it (the fleet adapter is typically driven via
rmf_fleet_adapter_python), so it's an uncaught exception →std::terminate→ the entire process SIGABRTs (exit code -6), taking every robot in that fleet offline until the process is relaunched.Additional information or screenshots
Fix — matches the key convention already used for every other entry in
phaseswithin the same function: