Blackboard decorator node does not abort lower-priority nodes

Hello,

I came across an unexpected behavior with blackboard decorators in a specific scenario. I was able to reproduce it in a very simple project.

The setup is as follows:

[Image Removed]

  • AAA = true by default
  • BBB = true false by default

Both blackboard decorators use Observer Aborts = Lower Priority.

BTTask_Test only sets AAA to false, then back to true, and waits, as shown below.

[Image Removed]

When the behavior tree runs:

  1. BTTask_Test starts executing.
  2. AAA is set to false.
  3. After some time, AAA is set back to true
  4. While BTTask_Test is still active, the value of BBB is changed to true by game logic.

At this point, I would expect BTTask_Test to be aborted and the Wait task to start executing. However, this does not happen. I am trying to determine whether this behavior of blackboard decorators is correct or not.

From the UE source code, it appears that when AAA is set to false, the second blackboard decorator becomes not relevant. However, when AAA is set back to true, it does not become relevant again.

Is this expected behavior, or is it a bug?

[Attachment Removed]

Hi there,

Thanks for the report and repro steps.

After some testing, I was unable to reproduce the issue in either 5.6 or 5.7. In both versions, the Behavior Tree executes as expected. The Wait task begins execution after BTTask_Test is aborted.

[Image Removed]

Are there any additional settings or conditions that might be required to reproduce this behavior? Alternatively, if you’re able to provide a minimal repro project, that would greatly help us evaluate the issue further.

Best regards,

Henry Liu

[Attachment Removed]

Sorry, BBB should be false by default.

When BBB is set to true by the game logic in step 4, BTTask_Test should be aborted and Wait should start running, but this does not happen:

[Image Removed]I’ve uploaded a repro project.

The issue occurs when you:

  1. Run the game
  2. Wait a few seconds until the last delay in BTTask_Test is active (the “PRESS SPACE” debug string is displayed)
  3. Press the spacebar (which sets BBB to true)

At this point, BTTask_Test is not aborted as expected.

[Attachment Removed]

Hi,

Thanks for the repro project.

After running it in UE 5.6, I’m seeing the following behavior, after pressing Space, BTTask_Test is aborted once the 10-second delay completes, and the Wait task executes afterward. This matches what I observed in earlier testing, where BBB is set to true after 8 seconds, which appears to line up with the timing of pressing Space.

Please let me know if this matches the result you were expecting.

Best regards,

Henry Liu

[Attachment Removed]

Hi,

Yes, the behavior I’m experiencing is the same as the one you described: BTTask_Test is aborted once the 10-second delay completes, and then the Wait task executes.

However, the behavior I expected is different: BTTask_Test should be aborted immediately when BBB is set to true, rather than waiting for the 10-second delay to finish.

[Attachment Removed]

Hi

Thanks for the reply.

I understand the expected behavior. However, we might consider an alternative approach for this case.

Instead of relying on Delay node, we could introduce an explicit waiting state and waiting duration in the Blackboard data. A Selector could then decide whether to execute a Waiting task or a Completed task based on the current Blackboard values.

With this approach, the Behavior Tree would continuously evaluate the data and dynamically choose which node should be active. This makes the logic more reactive and data-driven.​

Using a Delay node implies that the task is still running and does not intend to finish during that period. It does not function well as a mechanism for waiting on a value to change, since it prevents the tree from re-evaluating conditions while the delay is in progress.

​This alternative would provide clearer intent and more flexible control over state transitions.

Hope this clarifies this case. Let me know if you have any further questions.

Cheers,

Henry Liu​

[Attachment Removed]

Hi,

The setup I described with Delay and Wait tasks is only a simple example illustrating the issue with aborting a lower-priority task using a decorator. Our actual Behavior Trees are much more complex, with many branches, tasks, and decorators. The example is purely to demonstrate what appears to be inconsistent abort semantics.

To further isolate the issue, I made a small change in the repro project:

  1. AAA is set to false by default
  2. BBB is set to true by default
  3. BTTask_Test waits a few seconds, sets AAA to true, and then waits a few more seconds

In this configuration, BTTask_Test is aborted immediately when AAA becomes true, which is the behavior I would expect when a higher-priority branch becomes valid.

However, in the original configuration, where the logical structure is effectively equivalent but the initial values differ, the lower-priority task is not aborted immediately when BBB becomes true.

This suggests that under very similar logical conditions, the system sometimes reacts immediately to Blackboard changes and sometimes does not.

Could you clarify whether this difference in behavior is intentional, or whether there may be an issue related to decorators?

[Attachment Removed]

Thank you for the additional information.

Based on the current setup, the behavior appears to be working as intended. Since the blackboard observing AAA, once AAA is set to true, the task is aborted immediately. Following this same logic, if there is a blackboard also observes BBB, the task would be aborted immediately when BBB becomes true. Like this:

[Image Removed]If the blackboard does not observe the relevant data, it has no way of knowing that current task should be aborted. This is what happened in the previous case.

In this case, the selector branch with blackboard AAA has a higher priority than BTTask_Test. The higher-priority branch can immediately interrupt a currently running lower-priority branch (BTTask_Test) when the condition changes. An alternative approach could be as follow:

[Image Removed]​

I hope this clarifies the situation. Please let me know if you have any further questions.

Best regards,

Henry Liu

[Attachment Removed]

Hi,

Thanks for your continued responses and suggestions, but I think there may still be a misunderstanding regarding what I’m trying to clarify.

My question is not about Behavior Tree design patterns (e.g. whether Delay is an appropriate waiting mechanism, or whether a more data-driven structure would be preferable).

The core issue I’m trying to understand is this:

In two logically equivalent setups — differing only in initial Blackboard values — the system reacts differently to runtime changes:

  • In one case, when a higher-priority branch becomes valid due to a Blackboard value change, the currently running lower-priority task is aborted immediately.
  • In the other case, under effectively the same logical conditions, the lower-priority task is not aborted when the higher-priority branch becomes valid, even though the relevant decorator is configured with Observer Aborts = Lower Priority.

In the screenshot below, you can see that second blackboard decorator also has Observer Aborts = Lower Priority, has higher priority than BTTask_Test, and also you can see that BTTask_Test is highlighted in cyan when the decorator is selected, which suggests it should be aborted:

[Image Removed]

This suggests that the difference in behavior is not due to tree structure or task design, but rather to how decorator relevance and Blackboard observation are handled when conditions change at runtime.

So the question I’m trying to resolve is:

Is it intentional that a decorator which became irrelevant due to a prior condition change does not re-evaluate (or re-register observation) when its gating condition becomes valid again — even though this would make a higher-priority branch immediately selectable?

In other words, I’m trying to determine whether this difference in abort timing is expected engine behavior, or whether it may indicate an issue with how decorator observers are restored when branch relevance changes dynamically.

Why is the second decorator unable to abort the lower-priority task even though all abort conditions appear to be met?

I appreciate any clarification on this specific point.

[Attachment Removed]

Hi,

Thank you for the update and the additional explanations. They have been greatly helpful in our investigation of this issue.

After further testing in UE and reviewing the source code, I believe this could be an engine bug. I’ll update this case with the issue tracker link once it’s available.

With the default values for AAA=true, BBB = false.

If AAA remains unchanged, the active task (BPTask_Test) can be aborted immediately by setting BBB to true.

However, if AAA is changed from true → false → true, the active task can no longer be aborted immediately when BBB is set to true.

Based on debugging, the behavior appears to be as follows:

When AAA is set to false, the corresponding sub-branch is deactivated, and the observer delegate for BBB is removed.

When AAA is set back to true, the behavior tree attempts to re-evaluate the branch. However, since CurrentActiveNode remains unchanged, the search is treated as invalid. The search data is rolled back (as indicated by the log message: “Search is not valid, reverted all changes”).And the observer delegate for BBB is not re-added.

As a result, setting BBB = true no longer aborts the CurrentActiveNode.

This leads to inconsistent behavior in the behavior tree solely due to changes in Blackboard values, even though the Blackboard state is identical.

A reasonable workaround is first check whether NextTask is the same as ExecutionRequest.SearchEnd in UBehaviorTreeComponent::ProcessExecutionRequest.

Like this:

if (ActiveInstanceIdx == ExecutionRequest.SearchEnd.InstanceIndex
		&& NextTask->GetExecutionIndex() == ExecutionRequest.SearchEnd.ExecutionIndex)
	{
		bIsSearchValid = true;
	}
	else
	{
		bIsSearchValid = NextTaskIdx.TakesPriorityOver(ExecutionRequest.SearchEnd);
	}

I hope this provides clarification. Please let us know whether this resolves the issue or if you have any additional questions.

Best regards,

Henry Liu

[Attachment Removed]

Hi,

I have submitted a bug report for this issue, and once it is reflected publicly, you will be able to view it here: https://issues.unrealengine.com/issue/UE-365889

An engine developer will investigate the bug at a later date. We don’t provide updates on EPS, but progress can be followed on that public issue tracker page. I will go ahead and close this case now, but feel free to respond here if you have any follow up questions.

Thanks,

Henry Liu

[Attachment Removed]