Setting ProcessMode occasionally triggers error: `NativeCalls.cs:331 @ void Godot.NativeCalls.godot_icall_1_37(nint, nint, int): Condition "p_elem->_root" is true.>`

Godot Version

v4.2.1.stable.mono.official [b09f793f5]

Question

I’m using ProcessMode to pause/resume two specific nodes during runtime (one of them is paused while the other is unpaused). My code is working, but occasionally I see the below error popping up:

E 0:02:10:0481   NativeCalls.cs:331 @ void Godot.NativeCalls.godot_icall_1_37(nint, nint, int): Condition "p_elem->_root" is true.
  <C++ Source>   ./core/templates/self_list.h:46 @ add()
  <Stack Trace>  NativeCalls.cs:331 @ void Godot.NativeCalls.godot_icall_1_37(nint, nint, int)
                 Node.cs:1356 @ void Godot.Node.SetProcessMode(Godot.Node+ProcessModeEnum)
                 Node.cs:398 @ void Godot.Node.set_ProcessMode(Godot.Node+ProcessModeEnum)
                 MyGame.cs:167 @ void MyGame.Screens.MyGame.MyGame.OnGamePhasePauseStateChanged(object, bool)
                 GamePhaseDirector.cs:29 @ void MyGame.Screens.MyGame.GamePhaseDirector.set_Paused(bool)
                 GamePhaseDirector.cs:100 @ void MyGame.Screens.MyGame.GamePhaseDirector.ActivateGamePhase(MyGame.Screens.MyGame.GamePhaseDirector+GamePhaseTimerValues, System.Nullable`1[MyGame.Screens.MyGame.GamePhase])
                 MyGame.cs:180 @ void MyGame.Screens.MyGame.MyGame.OnCameraTransitionFinished(object, System.EventArgs)
                 HeroCamera.cs:108 @ void MyGame.Screens.MyGame.HeroCamera.<SetCameraMode>b__26_0()
                 HeroCamera.cs:91 @ void MyGame.Screens.MyGame.HeroCamera+<CameraTransitionToWorldView>d__25.MoveNext()
                 :0 @ void System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1.ExecutionContextCallback(object)
                 :0 @ void System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, object)
                 :0 @ void System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1.MoveNext(System.Threading.Thread)
                 :0 @ void System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1.MoveNext()
                 SignalAwaiter.cs:58 @ void Godot.SignalAwaiter.SignalCallback(nint, Godot.NativeInterop.godot_variant**, int, Godot.NativeInterop.godot_bool*)

The referenced code in MyGame@176 looks like this:

myNode1.ProcessMode = isPhase1 ? ProcessModeEnum.Disabled : ProcessModeEnum.Inherit;
myNode2.ProcessMode = !isPhase1? ProcessModeEnum.Disabled : ProcessModeEnum.Inherit;

The game continues to run, even with a non-debug build, but I’m uncomfortable with having mysterious errors.

The closest GitHub issue I could find was this: add: Condition "p_elem->_root" is true. error spam in connection with VisibleOnScreenEnabler2D · Issue #80073 · godotengine/godot · GitHub

What alternatives could I try to have a specific node (along with its child nodes) pause all its processing (including navigation, input and physics) instead of setting ProcessMode?

I have tried the usual tricks like skipping a few frames and using native C# events instead of signals, but it had no effect on this error.

the alternative I use is SetProcess, SetPhysicsProcess and SetProcessInput. there are other calls but I don’t think I need to use them. And I put them in an extension method so i can v_my_node.Active(false) or whatever.

public static class ExtensionMethods {
	public static void Active(this Node a_node, bool a_active) {
		a_node.SetProcess(a_processing);
		a_node.SetPhysicsProcess(a_processing);
		a_node.SetProcessInput(a_processing);
	}
}

here is a link to the other calls just in case.

Thank you for the suggestion!

I was experimenting with that myself, but there were two problems with this approach.

First, I’d have to recursively apply this call to all child nodes, and if either child wants local control over any of these aspects, it’d increase the complexity.

Second, the node, which had a non-zero Velocity would just keep on travelling. I tried to apply the process override calls to the parent node as well, but it made no difference.

Edit: It looks to me, unless I explicity set the NavigationAgent3D node’s ProcessMode to Disabled while it’s supposed to be not processing, it’d just keep on applying the latest Velocity value. Does this sound the expected behavior? Am I supposed to pause the navigation agent elsewhere?

Maybe the error is unrelated to ProcessMode.

I’m now able to reproduce the error.

My game cycles camera position between two states: it’s a top-down view in one state, and a “traditional” perspective camera that you’d use in 3rd person view games.

During development I’m using the mouse_entered and mouse_exited signals to show diagnostics data regarding the hovered ground element. Obviously, the hovered ground node also changes when the camera makes its transition. So, when I didn’t have the mouse inside the game window, I haven’t seen the error; but if I intentionally left the mouse hovering over a ground node that was no longer under the mouse after the end of the camera transition, the error has appeared right away.

I tried to use native C# events instead of signals but it didn’t have any effect.

Now, the error points to the following line in Godot’s code:

Unfortunately, I don’t know nearly enough about C++ or the Godot source to understand what countermeasures I should implement. I’m not sure if my game will need to use mouse hover, but I’d still like to understand the context this error is complaining about. I’m hoping someone with more knowledge about this part of Godot would be able to help me to figure out how it’d be best to handle the situation and if it’s something that should be reported as a bug.

in c# we typically use List. in c++ writing your own List isn’t unheard of. The error message is displayed because the root node is NOT null because it already has a value. if c# we have to write code if(null == v_ptr) or if(0 == v_int) but in c++ you can write if(!v_ptr) or if(!v_int).

ERR_FAIL_COND(p_elem->_root);

here is the macro that is defined in: error_macros.h:

#define ERR_FAIL_COND(m_cond)                                                                          \
	if (unlikely(m_cond)) {                                                                            \
		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true."); \
		return;                                                                                        \
	} else                                                                                             \
		((void)0)

maybe that helps?

I appreciate your willingness to help, but what I meant is that I don’t know enough about C++ to be able to read the Godot source in a way that would allow me to understand what’s happening and causing the exception I’m getting.

At this point, I’m unsure about the circumstances that trigger it, so I don’t know how I should modify my own code to avoid this bug. Perhaps I need to temporarily disable or delay certain features (as these types of hacks often work with various Godot bugs), but right now, I’m unable to find NativeCalls.cs or Node.cs, which are mentioned in the stack trace, neither on Repositories · GitHub nor in my local environment.

What the error message is saying is an element being added to a linked list, is already an element of a linked list. That gives me an idea. In your original post you said:

I’m using ProcessMode to pause/resume two specific nodes during runtime (one of them is paused while the other is unpaused).

If you are resuming a paused node/pausing a running node in the same frame, this might confuse godot.

My recommendation is first pause the running node. Now both nodes are paused. Then use CallDeferred to resume the other node. this will give Godot a moment to sort everything out.

Here is a c# example that demonstrates what I am thinking.

using Godot;
using System;
public partial class test:Node2D {
	Node m_pause_screen_node = new Node();
	Node m_game_screen_node = new Node();

	void DisableGameScreenResumePauseScreen(bool a_game) {
		if(true == a_game) {
			m_game_screen_node.ProcessMode = ProcessModeEnum.Disabled;
			CallDeferred("DisableGameScreenResumePauseScreen", false);
		}
		else {
			m_pause_screen_node.ProcessMode = ProcessModeEnum.Inherit;
		}
		GD.Print("game screen:", m_pause_screen_node.ProcessMode, " pause screen:", m_game_screen_node.ProcessMode);
	}

	public override void _Ready() {
		AddChild(m_pause_screen_node);
		m_pause_screen_node.ProcessMode = ProcessModeEnum.Disabled;

		AddChild(m_game_screen_node);
		m_game_screen_node.ProcessMode = ProcessModeEnum.Inherit;

		GD.Print("game screen:", m_pause_screen_node.ProcessMode, " pause screen:", m_game_screen_node.ProcessMode);

		CallDeferred("DisableGameScreenResumePauseScreen", true);
	}
}


And I just remembered, another way to ‘disable’ a node is to remove it from the tree. If a node is not part of a tree, it just chills in memory, doing nothing. The drawback is _Ready() gets called anytime a node get added to a tree.

While searching Condition “p_elem->_root” is true I noticed you posted elsewhere on reddit and github a couple weeks ago. Sorry this has been giving you problems for a while, wishing you the best of luck on this!

1 Like

A splendid idea! And also, that was the solution. :slight_smile:

As you have suggested, with the usage of CallDeferred I no longer see the error, not even when I restored the mouse hover functionality as well.

I remember encountering a similar problem when I was using a C# List with a LINQ expression, and I had to convert that to an array in order to prevent the collection from being changed during the iteration. Thankfully, in this particular case deferring the unpausing of the other node is completely fine. Just for science I also tested with skipping a frame with await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);, and this was also okay for my situation, but your suggestion is better.

Thank you very much for your persistent help!

1 Like

Well, rarely the same error appears again when I’m setting ProcessMode to ProcessModeEnum.Inherit, even when skipping several frames. It’s very strange though the hack with the delays had some influence as I can see it less often, the bug still persists.

I guess I’ll ignore it for the time being. Maybe v4.3, which is probably getting released next month, will entertain me with some other riddle.

okay, so i wrote a test add add/remove child do not call _Ready() each time the child node is re-added. and i confirmed that _Process() is not called when a child has no parent. This might be a viable alternative, if necessary.

if CallDeferred() reduced errors you see but you still get a few stray errors, then something must still be processing. Maybe a rogue timer or camera.


using Godot;
using System;
public partial class MyTest:Node2D {
	public MyTest(Node2D a_parent, string a_name) {
		Name = a_name;
		a_parent.AddChild(this);
	}
	public override void _Ready() {
		GD.Print("ready:", Name);
	}
	public override void _Process(double a_delta) {
		GD.Print("process:", Name);
	}
}
public partial class MyRoot:Node2D {
	MyTest test = null;
	public void AddTest() {
		GD.Print("------------- add child --------------");
		AddChild(test);
	}
	public void RemoveTest() {
		GD.Print("------------- remove child --------------");
		RemoveChild(test);
		CallDeferred("AddTest");
	}
	public void RemoveTestAgain() {
		GD.Print("------------- remove child again --------------");
		RemoveChild(test);
	}
	public override void _Ready() {
		test = new MyTest(this, nameof(test));
		MyTest __test_1 = new MyTest(test, nameof(__test_1));
		MyTest ____test_1_2 = new MyTest(__test_1, nameof(____test_1_2));
		MyTest __test_2 = new MyTest(test, nameof(__test_2));
		MyTest ____test_2_1 = new MyTest(__test_2, nameof(____test_2_1));
		MyTest ____test_2_2 = new MyTest(__test_2, nameof(____test_2_2));
		MyTest ______test_2_2_1 = new MyTest(____test_2_2, nameof(______test_2_2_1));
		CallDeferred("RemoveTest");
	}
	public override void _Process(double delta) {
		if(this == test.GetParent()) {
			CallDeferred("RemoveTestAgain");
		}
	}
}

1 Like

In my case I cannot remove the paused node from the scene tree because the nodes have to remain visible, but I haven’t seen the bug since yesterday evening, so maybe it was just some caching issue – Godot 4.2 has this known bug with C# scripts occasionally getting “reverted” from cache. I believe this is going to be fixed in 4.3.

Still, I remain very grateful for your continuous support. Your behavior is both exemplary and inspirational. Thank you!

This thread is the only thing I’ve seen that talks about this issue in a usable way. You two have given me a lot to try and pinpoint an answer to my problem. I believe I’m experiencing essentially the same issue, and it’s been driving me nuts trying to work it out. I get this exact error, same source line and everything, sometimes when I use set_process_mode(PROCESS_MODE_INHERIT) on one of my characters.

The game context is the Player is controlling an Avatar (2 separate nodes), and when Avatar_A dies, a different Avatar_B takes its place. Avatar_A does some cleanup logic, and when that logic ends, it remains on the screen as a corpse and gets call_deferred("set_process_mode", PROCESS_MODE_DISABLED). Avatar_B is put in place and gets set_process_mode(PROCESS_MODE_INHERIT) then immediately begins game action. The error occurs on that set_process_mode line. I could set Avatar_B to also defer its call, but this then causes other errors that I’d need to figure out other solutions to, so I’m avoiding that unless it’s the only solution and trying to do set_process_mode(PROCESS_MODE_INHERIT) is just cursed and should include documentation to just not be used.

For additional context, the game contains numerous Avatar_A’s at any given time, and any of them could die and be resolving their set_process_mode() calls. Since you mentioned multiple nodes doing this on the same frame could be a culprit, I’m wondering about this happening, but currently all of them are set to call_deferred() except for this one instance of Avatar_B.

The particularly perplexing thing is that my game has a replay system, so I’m able to use that for helping with debugging, and the spots where this error occurs is inconsistent. I’d say it occurs maybe 5% of the time or so in general when the Avatar_A is killed, but the same event that will cause it in one viewing of the replay won’t cause it in the same spot in the next.

However, I do finally have a replay that has an event where this bug is being reproduced 100% of the time. I’ve got breakpoints set for the line immediately before the error occurs, and immediately after, but I don’t know what to look for to find a culprit. As far as I can tell, the node and all of its children are all disabled. I even manually disabled all of them at the breakpoint just to be extremely sure, and no dice.

Any suggestions?