What is the cause of this “Invalid get index… on previously freed instance” error in Godot

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Diet Estus

I am trying to write a scene manager in Godot and keep getting the following error:

Invalid get index… (on base: ‘previously freed instance’)

I think this error is generated because certain nodes in my scene are trying to access attributes of other nodes which were just freed in my scene change code.

Here is the scene change code which is called from a globals.gd singleton:

func set_scene(scene_path):
    # remove current scene
    current_scene.queue_free()
    print("FREED SCENE")
    # instance new scene
    var s = load(scene_path)
    current_scene = s.instance()
    print("INSTANCED SCENE")
    # add scene instance to tree	    
    get_tree().get_root().add_child(current_scene)
    print("ADDED SCENE TO TREE")

These print statements confirm that the error is being thrown after the new scene is added to the tree. In fact, the error always has to do with references to attributes of other nodes that are accessed via variables.

My question is: Why is this error being thrown even when I free everything from the previous scene before I instance the new scene?

I may be able to protect my code from this kind of error using weakref(), as mentioned here, but I really would prefer to avoid this since it muddies up the code. I have also tried to use call_deferred() with set_scene() but it doesn’t help.

NOTE: I originally asked this question on the Game Dev Stack Exchange, but figured this would be a better venue. I will echo any answer I get on either site.

hm. I did a similar thing as you did. but I don’t have such problem.
did you try it with a just empty scene?

volzhs | 2018-03-11 10:15

:bust_in_silhouette: Reply From: Footurist

Tested it with one of my scenes and it works as follows:

extends Node2D

onready var current_scene = $Cube

func set_scene(scene_path):
	# remove current scene
	current_scene.queue_free()
	print("FREED SCENE")
	# instance new scene
	var s = load(scene_path)
	current_scene = s.instance()
	print("INSTANCED SCENE")  
	call_deferred("_add_new")  
   
func _add_new():
	# add scene instance to tree  
	get_tree().get_root().add_child(current_scene)
	
func _ready():
	set_scene("res://scenes/Cube.tscn")

If this won’t work , try this:

extends Node2D

onready var current_scene = $Cube

func set_scene(scene_path):
	# remove current scene
	current_scene.queue_free()
	print("FREED SCENE")
	yield(current_scene, "tree_exited")
	# instance new scene
	var s = load(scene_path)
	current_scene = s.instance()
	print("INSTANCED SCENE")  
	get_tree().get_root().add_child(current_scene) 
   
func _ready():
	set_scene("res://scenes/Cube.tscn")

Both should work. However, call_deferred logically only works if you wrap only add_child in it, because that’s the part that has to wait for the old scene to be removed completely and the future references not to be “fighting” with old references.