Skip to main content

Migrating from version 3.X to 4.X

You will find that most of the changes in version 4.X are incremental and back compatible with your previous code.

Here we try to summarize the most relevant difference that you should be aware of, when migrating.


In the repository you can find a Python script called that may save you some time (thanks to use!

Try it, but make sure that you double check the result first!

Class renaming

The name of the following classes / XML tags changed.

Name in 3.8+Name in 4.xWhere
SequenceStarSequenceWithMemoryC++ and XML

If you want to quickly fix the compilation of your C++ code, even if refactoring is encorauged, do:

namespace BT 
using NodeConfiguration = NodeConfig;
using AsyncActionNode = ThreadedAction;
using Optional = Expected;

These changes can be disabled while compiling BT.CPP with the CMake option USE_V3_COMPATIBLE_NAMES.


You should add the attribute BTCPP_format to the \<root> tag of your XML:




<root BTCPP_format="4">

This will allow us to be compatible with both versions 3 and 4 (in future releases, not yet).

SubTree and SubTreePlus

The deafault SubTree in 3.X has been deprecated and SubtreePlus is the new default, being easier to use and more consistent.

Name in 3.8+Name in 4.x

SetBlackboard and BlackboardPrecondition

The new scripting language is much simpler and more powerful.

Old code in 3.8+:

<SetBlackboard output_key="port_A" value="42" />
<SetBlackboard output_key="port_B" value="69" />
<BlackboardCheckInt value_A="{port_A}" value_B="{port_B}"

New code in 4.X:

<Script code="port_A:=42; port_B:=69" />
<Precondition if="port_A==port_B" else="FAILURE">

Preemptable Nodes and Tree::tickRoot()

A serious problem was detected by a user here:

If a ControlNode or DecoratorNode has synchronous children only, it is impossible to interrupt it.

Consider this example:

<Sequence name="synch_sequence">

Once the Sequence "synch_sequence" starts, with BT.CPP 3.X it is impossible for AbortCondition to stop it.

In BT.CPP 4.X we modified our Controls and Decorators to prevent this potential issue.

Now, when a Synchronous child is executed, RUNNING is returned before moving to the next child. In this way, we give the opportunity to the tree to check ReactiveSequences or other Conditions.

From a practical point of view, this means that we must call tick() more often.


This new behavior should NOT introduce any additional latency, at least not a significant one.

When Controls and Decorator return RUNNING, the method Tree::sleep() will not block and won't introduce any additional delay. This is the reason why you should never use "normal" sleep functions.

To make this new behavior more explicit, the method Tree::tickRoot() was removed, and we introduce these two new methods instead:

  • Tree::tickOnce() works as usual. It should run inside a while-loop.
  • Tree::tickWhileRunning() has its own while-loop, and will continue ticking until either SUCCESS or FAILURE is received.