During the HUD step of the 2D tutorial, I encountered the following errors:
E 0:00:06:0368 main.gd:58 @ _on_score_timer_timeout(): Node not found: "HUD" (relative to "/root/Main").
<C++ Error> Method/function failed. Returning: nullptr
<C++ Source> scene/main/node.cpp:1793 @ get_node()
<Stack Trace> main.gd:58 @ _on_score_timer_timeout()
and
E 0:00:07:0214 main.gd:19 @ game_over(): Node not found: "HUD" (relative to "/root/Main").
<C++ Error> Method/function failed. Returning: nullptr
<C++ Source> scene/main/node.cpp:1793 @ get_node()
<Stack Trace> main.gd:19 @ game_over()
player.gd:48 @ _on_body_entered()
The Issue
The problem was that I had accidentally added the HUD scene to the “mobs” group. In my new_game()
function, I had this code:
func new_game():
print("in new_game")
score = 0
$Player.start($StartPosition.position)
$StartTimer.start()
$HUD.update_score(score)
get_tree().call_group("mobs", "queue_free")
$HUD.show_message("Get Ready")
print("HUD is still set: ", $HUD)
The call to get_tree().call_group("mobs", "queue_free")
was intended to free all mob nodes. However, because the HUD was mistakenly a member of the “mobs” group, it was queued for deletion. Godot uses deferred deletion, meaning that nodes marked with queue_free()
are not immediately removed but are deleted at the end of the current frame. This is why the HUD reference appeared valid immediately after the call, but was null by the next frame when accessed again.

Debugging the Issue
I added debug prints such as print($HUD)
and even checked the groups with get_groups()
on the HUD node. The outputs confirmed that the HUD was indeed part of the “mobs” group. Additionally, placing a print in the HUD’s _exit_tree()
method would reveal when the node was being freed, which can be invaluable in tracking down such issues.
Best Practices to Avoid Similar Issues
- Group Management:
Always ensure that UI elements, like the HUD, are kept in separate groups from gameplay elements. This prevents accidental deallocation when freeing nodes from a group. - Verify Group Membership:
Use the Godot editor or code ($HUD.get_groups()
) to check which groups a node belongs to. If needed, remove a node from a group with$HUD.remove_from_group("mobs")
. - Deferred Deletion Awareness:
Understand thatqueue_free()
does not remove a node immediately. This deferred deletion can cause references to seem valid momentarily even after being marked for deletion.
The Resolution
Removing the HUD scene from the “mobs” group fixed the issue. This experience highlighted the importance of carefully managing node groups and understanding deferred deletion in Godot.
Leave a Reply