Skip to content

[Bug]: TaskManager::publish_task_state() throws type_error.305 on skip/rewind — inconsistent phases[] key type #542

Description

@HappySamuel

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

  1. Dispatch any compose/patrol task with 2+ phases.
  2. 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}
  3. 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"];

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status
    Inbox

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions