Why is there a problem about 'running a base null_instance on a null_instance'?

Hi, I’m running on Godot Version 4.4

I’m new, and I’m trying to make a 2D platformer. I’ve recently been trying to implement the health bar, but it causes an error. I’m confused as to what is happening.
Here’s my script:
This chunk is in the player node:
var health = 100

func _ready() → void:
health = 100
health_bar._init_health(health)

This chunk is in the healthbar node:
func _init_health(_health) → void:
health = _health
max_value = _health
value = _health
damage_bar.max_value = health
damage_bar.value = health

The error says: Attempt to call function ‘Init Health’ in base ‘null_instance’ on null_intance
I got this code from a tutorial, which says it’s running in Godot 4, so I’m not sure why it’s not working.

If you put three backticks (```) on lines before and after your code, it formats more nicely:

# Player Script
var health = 100

func _ready() -> void:
    health = 100
    health_bar._init_health(health)

# Healthbar Script

func _init_health(_health) -> void:
    health = _health
    max_value = _health
    value = _health
    damage_bar.max_value = health
    damage_bar.value = health

I presume the error is that health_bar in the player script isn’t initialized, but it’s not listed in the code you posted. If it were a node, you might be able to refer to it via $ notation, or get access to it via get_node().

Something has to tell health_bar in the player script where to look for the health bar object so it can find the _init_health() function to execute.

Thanks but the healthbar node is already listed in the player script. Here’s my WHOLE player script (I wrote this about a year ago so it may be bad)
Here’s the player code:

extends CharacterBody2D


const SPEED = 125.0
const JUMP_VELOCITY = -300.0
@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D
@onready var hurtbox: CollisionShape2D = $Hurtbox/CollisionShape2D
@onready var killzone: Area2D = $"../Killzone"
@onready var health_bar: ProgressBar = $Hurtbox/HealthBar



const DASH_SPEED = 300
var dashing: bool = false
var can_dash: bool = true
var gravity = 900
var health = 100

func _ready() -> void:
	health = 100
	health_bar._init_health(health)

func _physics_process(delta: float) -> void:
	# Add the gravity.
	if not is_on_floor() or not dashing:
		velocity.y += gravity * delta
		
	
	

	if Input.is_action_just_pressed("Dash") and can_dash:
		dashing = true
		can_dash = false
		$Timer.start()
		$CanDashTimer.start()
	

	# Handle jump.
	if Input.is_action_just_pressed("Up"):
		if is_on_floor():
			velocity.y = JUMP_VELOCITY
		else:
			if dashing:
				velocity.y = DASH_SPEED - DASH_SPEED - DASH_SPEED
	

	
	
	var direction := Input.get_axis("Left", "Right")
	if direction:
		if dashing:
			velocity.x = direction * DASH_SPEED
		else:
			velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)

	move_and_slide()
	

	
	
	if is_on_floor():
		if direction == 0:
			animated_sprite.play("idle")
		else:
			animated_sprite.play("run")
	
		
	if dashing:
		animated_sprite.play("dash")
	
	
	if direction > 0:
		animated_sprite. flip_h = false
	elif direction < 0:
		animated_sprite. flip_h = true

	
	
	#Handles health and death
	
	health_bar.health = health
	
func _on_hurtbox_area_entered(_killzone) -> void:
	health = health - 15
	animated_sprite.play("temporary")
	velocity.y = -600
	
	
	
#Makes dash stop
func _on_timer_timeout() -> void:
	dashing = false
	
func _on_can_dash_timer_timeout() -> void:
	if is_on_floor():
		can_dash = true

Here’s the Healthbar code:

extends ProgressBar


@onready var timer: Timer = $Timer
@onready var damage_bar: ProgressBar = $DamageBar


var health = 0 : set = _set_health

func _set_health(new_health):
	var prev_health = health
	health = min(max_value,new_health)
	value = health
	
	if health <= 0:
		queue_free()
		
	if health < prev_health:
		timer.start()
	else:
		damage_bar.value = health

func _init_health(_health) -> void:
	health = _health
	max_value = _health
	value = _health
	damage_bar.max_value = health
	damage_bar.value = health

func _on_timer_timeout() -> void:
	damage_bar.value = health

Thanks for the formatting tip by the way!

Your problem may be:

@onready var health_bar: ProgressBar = $Hurtbox/HealthBar

That @onready gets called (I believe) when the player node enters the scene tree, which may occur before the health bar node is instanced or added to the scene tree. Basically, you may be grabbing a reference to something that doesn’t exist yet.

You could try something like:

# Player script.

var health_bar: Node = null

[...]

func _process(delta: float) -> void:
    if !health_bar: health_bar = $Hurtbox/HealthBar
    [...]

Basically, have _process() check if it’s initialized and if not, fix that. You should only eat the node path lookup cost once, and a single branch won’t cost you much performance.

Thanks, but it still doesn’t work. Please let me know if I put it in the wrong spot (which I think I did). Here’s my player script (that has all the healthbar stuff in it):



const SPEED = 125.0
const JUMP_VELOCITY = -300.0
@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D
@onready var hurtbox: CollisionShape2D = $Hurtbox/CollisionShape2D
@onready var killzone: Area2D = $"../Killzone"
var health_bar: Node = null


const DASH_SPEED = 300
var dashing: bool = false
var can_dash: bool = true
var gravity = 900
var health = 100

func _process(delta: float) -> void:
	if !health_bar: health_bar = $Hurtbox/HealthBar

func _ready() -> void:
	health = 100
	health_bar._init_health(health)

Now the error shows:
Invalid call. Nonexistent function ‘_init_health’ in base ‘Nil’

Let’s walk through what’s likely wrong and how to fix it.


  1. What is health_bar?

You need to make sure that health_bar is a reference to your actual HealthBar node in the scene.

If it’s not assigned before calling _init_health, you’ll get that error.


  1. Fix: Assign the HealthBar node

Make sure to assign health_bar in your Player script before you try to use it.

If HealthBar is a child of the Player node in the scene tree, you can assign it like this:

@onready var health_bar = $HealthBar

Put that above your _ready() function in the Player script. The full code should now look like this:

@onready var health_bar = $HealthBar

var health = 100

func _ready() → void:
health = 100
health_bar._init_health(health)


  1. About the HealthBar Script

Your HealthBar script is mostly fine, just make sure the node it’s attached to (likely a TextureProgressBar or similar) has a child node named damage_bar if you reference it like this:

damage_bar.max_value = health
damage_bar.value = health

Or, also define damage_bar properly at the top:

@onready var damage_bar = $DamageBar


Summary:

Make sure health_bar is initialized with @onready and points to the correct node.

Avoid calling methods on null references.

Check your scene tree structure so node paths match.

I tried that but it STILL doesn’t work. Now the error shown is: Attempt to call function ‘_init_health’ in base ‘null instance’ on a null instance

AH.

Ok, something worth knowing: Things that start with an underscore can’t be called from outside the script. I bet if you rename your _init_health() function to init_health(), it will link up properly.

Nope. Same error

In that case, try removing the init health call from _ready() and do something like:

# Player script.

var health_bar: Node = null

func _process(delta: float) -> void:
    if !health_bar:
        health_bar = $Hurtbox/HealthBar
        health_bar.init_health(health)

_init_health() can’t be called from outside the script it’s defined in because the leading underscore marks it as internal-only.

Trying to call something on health_bar in the _ready() function runs the risk that the player’s _ready() script is called before the health bar is instantiated. You can’t be sure what the order of instantiation of objects is, and _ready() gets called when an object is instantiated and added to the tree, not when all objects are instantiated and added to the tree.

Again, same error

Hang on, I figured it out. I was connecting from a node that didn’t exist.

Turns out, I added a CanvasLayer node and the ‘onready’ call had to change, but I forgot to change it.

Thanks for all your help anyway!

1 Like

Is the $Hurtbox/HealthBar path correct? does the health_bar in the scene have a HealthBar name?

If node path is incorrect then get_node("Hurtbox/HealthBar") (and $Hurtbox/HealthBar) will return null and will not rise a warning/error

Try to set health_bar using inspector.
This should ensure that the existing node is selected:

# save changes and set health_bar in the inspector
@export var health_bar: ProgressBar

func _ready() -> void:
	health = 100
	health_bar._init_health(health)

I already figured out the issue.

Nice to hear. My answer was under moderation, so it didn’t appear relevant.

Thanks!