Skip to content

Commit 23dba60

Browse files
kineticsystemclaude
andcommitted
Halt the tree and complete the execution when a node throws
When a node threw an exception while the tree was ticked, execute() aborted the goal and returned at once: the tree was not halted, and onTreeExecutionCompleted() was not called. Nodes still running, e.g. an action whose goal was sent, were left running, and a server that cleans up or reports the end of a run in onTreeExecutionCompleted() never learned that the run had ended. Halt the tree and call onTreeExecutionCompleted(FAILURE, false) there too, as for every other end of a run, once onTreeCreated() has been called for the goal. The result also carries the FAILURE node status. An exception thrown before, e.g. by the creation of the tree, is handled as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 6c6aa07 commit 23dba60

3 files changed

Lines changed: 34 additions & 1 deletion

File tree

‎behaviortree_ros2/include/behaviortree_ros2/tree_execution_server.hpp‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ class TreeExecutionServer
125125

126126
/**
127127
* @brief onTreeExecutionCompleted is a callback invoked after the tree execution is completed,
128-
* i.e. if it returned SUCCESS/FAILURE or if the action was cancelled by the Action Client.
128+
* i.e. if it returned SUCCESS/FAILURE, if the action was cancelled by the Action Client,
129+
* or if a node threw an exception while the tree was ticked: the tree is then halted, and
130+
* the status is FAILURE.
129131
*
130132
* @param status The status of the tree after the last tick
131133
* @param was_cancelled True if the action was cancelled by the Action Client

‎behaviortree_ros2/src/tree_execution_server.cpp‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ void TreeExecutionServer::execute(
186186
executeRegistration();
187187
}
188188

189+
// Whether onTreeCreated() was called for this goal: only then is there a tree
190+
// of this goal to halt, and an execution to complete, if an exception is thrown.
191+
bool tree_created = false;
192+
189193
// Loop until something happens with ROS or the node completes
190194
try
191195
{
@@ -198,6 +202,7 @@ void TreeExecutionServer::execute(
198202

199203
// call user defined function after the tree has been created
200204
onTreeCreated(p_->tree);
205+
tree_created = true;
201206
p_->groot_publisher.reset();
202207
p_->groot_publisher =
203208
std::make_shared<BT::Groot2Publisher>(p_->tree, p_->params.groot2_port);
@@ -264,6 +269,29 @@ void TreeExecutionServer::execute(
264269
{
265270
action_result->return_message = std::string("Behavior Tree exception:") + ex.what();
266271
RCLCPP_ERROR(kLogger, action_result->return_message.c_str());
272+
status = BT::NodeStatus::FAILURE;
273+
action_result->node_status = ConvertNodeStatus(status);
274+
if(tree_created)
275+
{
276+
// A node that throws while the tree is ticked leaves the other nodes as
277+
// they were, e.g. an action whose goal still runs: halt them, as when the
278+
// execution is cancelled.
279+
try
280+
{
281+
p_->tree.haltTree();
282+
}
283+
catch(const std::exception& halt_ex)
284+
{
285+
RCLCPP_ERROR(kLogger, "Exception while halting the Behavior Tree: %s",
286+
halt_ex.what());
287+
}
288+
// The execution ends here too: let the user clean up, as for any other end.
289+
// Override the message value if the user defined function returns it
290+
if(auto msg = onTreeExecutionCompleted(status, false))
291+
{
292+
action_result->return_message = msg.value();
293+
}
294+
}
267295
goal_handle->abort(action_result);
268296
return;
269297
}

‎behaviortree_ros2/tree_execution_server.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ This happens if:
5353
1. Ticking the tree returns SUCCESS/FAILURE
5454
2. The `rclcpp_action::Client` cancels the action.
5555
3. Callback `onLoopAfterTick`cancels the execution.
56+
4. A node throws an exception while the tree is ticked. The tree is halted, and
57+
the callback receives FAILURE. It is not called when the exception is thrown
58+
before `onTreeCreated`, e.g. by the creation of the tree.
5659

5760
Argument `was_cancelled`is true in the 1st case, false otherwise.
5861

0 commit comments

Comments
 (0)