Why is my node null?
The error
Section titled “The error”You’ll see something like one of these in the Debugger panel. The node name and path will be your own:
Which cause is it?
Section titled “Which cause is it?”| What’s happening | Go to |
|---|---|
The error includes Node not found | The path is wrong |
The variable is declared at the top of the script with $ | Missing @onready |
It breaks right after you instantiate() a scene | Used before add_child |
| It works at first, then breaks during the game | The node was freed |
The variable uses @export | Export not assigned |
The example scene
Section titled “The example scene”All the fixes below use this scene. The script is on Main.
MainNode2D
script attached here
PlayerCharacterBody2D
UICanvasLayer
ScoreLabelLabelthe node we want
The path is wrong
Section titled “The path is wrong”$ is a shortcut for get_node(). These two lines do exactly the same thing, so every fix on this page works for both:
$UI/ScoreLabelget_node("UI/ScoreLabel")UI/ScoreLabel is a path relative to the node the script is on. It breaks if:
- the name doesn’t match exactly. Names are case-sensitive, so
$UI/scorelabelfails. - you renamed the node, or moved it to a different parent.
- the script is on a different node than you think.
Fix: let Godot write the path for you. Hold Ctrl and drag the node from the Scene dock into the script editor. Godot writes the whole line with the correct path:
extends Node2D
@onready var score_label: Label = $UI/ScoreLabel
func _ready() -> void: score_label.text = "Score: 0"using Godot;
public partial class Main : Node2D{ private Label _scoreLabel;
public override void _Ready() { _scoreLabel = GetNode<Label>("UI/ScoreLabel"); _scoreLabel.Text = "Score: 0"; }}Better fix: use a unique name (%)
Section titled “Better fix: use a unique name (%)”Right-click ScoreLabel in the Scene dock → Access as Unique Name. A % icon appears next to it. Now you can find it by name alone, wherever it is in the scene:
MainNode2D
PlayerCharacterBody2D
UICanvasLayer
TopBarHBoxContainer
ScoreLabelLabel
moved into TopBar, still found
@onready var score_label: Label = $UI/ScoreLabel@onready var score_label: Label = %ScoreLabel_scoreLabel = GetNode<Label>("UI/ScoreLabel");_scoreLabel = GetNode<Label>("%ScoreLabel");Moving ScoreLabel into TopBar broke $UI/ScoreLabel, but %ScoreLabel still works. With get_node(), put the % inside the quotes: get_node("%ScoreLabel").
Missing @onready
Section titled “Missing @onready”Variables at the top of a script are set before the node’s children exist. Without @onready, $UI/ScoreLabel runs too early and returns null:
var score_label: Label = $UI/ScoreLabel@onready var score_label: Label = $UI/ScoreLabel@onready waits until _ready(), when every child is in place.
Used before add_child
Section titled “Used before add_child”A scene you instantiate() isn’t in the game until you call add_child(). Until then its _ready() hasn’t run, so all its @onready variables are still null:
const ENEMY := preload("res://enemy.tscn")
func spawn() -> void: var enemy := ENEMY.instantiate() enemy.set_health(50) add_child(enemy) enemy.set_health(50)Fix: call add_child() first, then set things up.
The node was freed
Section titled “The node was freed”After queue_free(), any variable still pointing at that node is broken. The error mentions a previously freed object. Check before using it:
if is_instance_valid(target): target.take_damage(10)Export not assigned
Section titled “Export not assigned”@export var target: Node2D is null until you set it in the Inspector. Select the node and drag the target into the field. To catch it early, add an assert. The game then stops with a clear message instead of a confusing error later:
@export var target: Node2D
func _ready() -> void: assert(target != null, "Assign 'target' in the Inspector")Still stuck? Check the live scene tree
Section titled “Still stuck? Check the live scene tree”The end of the error tells you where Godot started looking: (relative to "/root/Main"). Run the game, then click Remote at the top of the Scene dock. That shows the tree as it really is while the game runs, so you can find your node and compare its actual name and path with what your code says.
If it’s still not obvious, add print_tree_pretty() to _ready() and read the tree in the Output panel.