Quick answer: Move node access to _ready(). Use _init() only for non-tree state. Pass dependencies via @export or constructor args.

You write a constructor that accesses $Sprite. Crashes with null. The sprite isn’t in the tree yet at _init time.

The Fix

extends Node2D

@onready var sprite: Sprite2D = $Sprite
@onready var tween_target: Vector2 = sprite.position

func _init():
    # sprite is null here — node tree not assembled
    # Use _init only for non-node setup
    pass

func _ready():
    # sprite is valid here
    sprite.modulate = Color.WHITE
    sprite.position = tween_target

The lifecycle: instantiate → _init → add to tree → _enter_tree → children added → _ready. Resolve children in _ready.

Verifying

Place sprite access in _ready: works. Same line in _init: null reference error. Confirms timing.

“_init for data. _ready for children. @onready waits.”

Related Issues

For C# signal emit, see signal emit. For Resource UID, see UID.

_ready is when. _init too soon.