How to resolve error "argument 1 (previously freed instance) is not a subclass of the expected argument class."

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

I am currently experimenting with the removal and reloading scenes and am using the following code, which is run in the Main Scene:

extends Node2D

var dialogue_getters = preload("res://src/dialogue_get_funcs.tscn").instance()
var dialogue_box = preload("res://src/dialogue_box_creator.tscn").instance()

func _input(event):
	if Input.is_key_pressed(KEY_UP):
		add_child(dialogue_getters)
		add_child(dialogue_box)
		var old_man_shouting = $Node2D/dialogue_line_get.get_line("old_man","shouting")
		$Dialogue_box_root/dialogue_box.dialogue = old_man_shouting
	elif Input.is_key_pressed(KEY_DOWN):
		dialogue_getters.queue_free()
		dialogue_box.queue_free()

When I run this, I press UP and I get the box appearing and the inserted dialogue showing up just fine. Similarly when I press DOWN the box disappears.

However, if I try to press UP again I get the following error message:

Invalid type in function 'add_child' in base 'Node2D (dialogue_main.gd)'. The Object-derived class of argument 1 (previously freed instance) is not a subclass of the expected argument class.

Which is confusing me as I don’t really understand what the error message is telling me.

I have tried noodilng around with the code, such as adding or replacing queue_free with “remove child” but if I understand correctly “remove_child” doesn’t actually free the scene from the memory (and if I just use remove_child and then re-add the child I just get a continuation of the current dialogue, rather than a restart).

I’ve tried to look everywhere but no one else seems to have this issue (It would be great if someone could point me to where I can read about reloading scenes in the documentation as well so I can better understand this part of the engine.)

:bust_in_silhouette: Reply From: Zylann

You are using .instance() from within the initializer of your member variables. This will run only once, when your script is loaded.
The first time you use add_child, it works fine, but then later you free them with queue_free(). Your member variables now reference a freed instance. This is why you can’t add it again, you should create a new instance.
Alternatively, you could consider using show() and hide().


Also, the input function is misused.

func _input(event):
    if Input.is_key_pressed(KEY_UP):
        add_child(dialogue_getters)

_input(event) fires for every input event: each time you press or release a keyboard key, each time a key-repeat occurs, or a mouse button, or each time the mouse moves or wheel moves etc.
Then you used Input.is_key_pressed. This will return true as long as the key is down. That means if two events, whatever they are, happen while your key is held, your code will run twice.
It looks like it works, but it’s luck. You may want to change it to:

func _input(event):
	if event is InputEventKey:
		if event.pressed and not event.is_echo():
			if event.scancode == KEY_UP:
				...
		    elif event.scancode == KEY_DOWN:
				...

Thank you for the reply and explanation but I think I am misunderstanding something regarding re-instancing because I already tried to reinstance the scene but I still get this error message:
The Code:

extends Node2D
#
var dialogue_getters = preload("res://src/dialogue_get_funcs.tscn")
var dialogue_box = preload("res://src/dialogue_box_creator.tscn")

func _input(event):
	if event is InputEventKey:
		if event.pressed and not event.is_echo():
			if event.scancode == KEY_UP:
				dialogue_getters.instance()
				dialogue_box.instance()
				add_child(dialogue_getters)
				add_child(dialogue_box)
				var old_man_shouting = $Node2D/dialogue_line_get.get_line("old_man","shouting")
				$Dialogue_box_root/dialogue_box.dialogue = old_man_shouting
			elif event.scancode == KEY_DOWN:
					dialogue_getters.queue_free()
					dialogue_box.queue_free()

The error message:

Invalid type in function 'add_child' in base 'Node2D (dialogue_main.gd)'. The Object-derived class of argument 1 (PackedScene) is not a subclass of the expected argument class.

If I have the .instance with the preloaded variables as well i just get this error message:

Invalid call. Nonexistent function 'instance' in base 'Node2D'.

Rickers | 2020-03-25 10:32

            dialogue_getters.instance()
            dialogue_box.instance()
            add_child(dialogue_getters)
            add_child(dialogue_box)

This is also wrong, you must use the result of instance(), here it’s being ignored.
Calling instance() on dialogue_getters does not turn dialogue_getters into an instance, it creates one and returns it.

extends Node2D

var dialogue_getters_scene = preload("res://src/dialogue_get_funcs.tscn")
var dialogue_box_scene = preload("res://src/dialogue_box_creator.tscn")

var dialogue_getters_instance
var dialogue_box_instance

func _input(event):
    if event is InputEventKey:
        if event.pressed and not event.is_echo():
            if event.scancode == KEY_UP:
                dialogue_getters_instance = dialogue_getters.instance()
                dialogue_box_instance = dialogue_box.instance()
                
                add_child(dialogue_getters_instance)
                add_child(dialogue_box_instance)
                
                var old_man_shouting = $Node2D/dialogue_line_get.get_line("old_man","shouting")
                $Dialogue_box_root/dialogue_box.dialogue = old_man_shouting

            elif event.scancode == KEY_DOWN:
                dialogue_getters_instance.queue_free()
                dialogue_box_instance.queue_free()

Zylann | 2020-03-25 23:38

Now it works perfectly, thank you so much for the detailed replies, i’ve learnt a great deal from this whole issue.

Rickers | 2020-03-26 15:05