Invalid access to property or key on a base object of type 'previously freed' in Godot 4

By Eduardo Orellana · Updated

The node your variable points at has been freed. Freeing a node, with free() or queue_free(), does not clear the variables that refer to it, so reading .position from one afterwards fails with this error. Check is_instance_valid() before using a reference that may outlive its node, and clear it when you free the node.

What Godot is checking

GDScript compiles enemy.position to a named property read. Before reading, the virtual machine checks that the object behind the reference still exists. When it was freed, there is no object to read from, the function stops, and Godot names the base 'previously freed'.

Freeing a node removes the object but does not reach the variables that refer to it. They keep the reference, and Godot names that case 'previously freed' instead of 'Nil'. On Godot 4.7.2 such a reference compares equal to null and is_instance_valid() returns false for it, as the fixed run below prints, but reading a property through it still stops the function.

queue_free() frees the node at the end of the current frame, so a reference read in the same frame still works and the error appears a frame later, often from a timer, a signal or _process.

Reproduce it

A script that makes a node, frees it, and then reads a property through the variable that still refers to it.

extends Node

var enemy: Node2D

func _ready() -> void:
	enemy = Node2D.new()
	add_child(enemy)
	enemy.free()
	print(enemy.position)
	print("after")

What Godot 4.7.2 prints

Godot Engine v4.7.2.stable.mono.official.ed1daf0bf - https://godotengine.org

SCRIPT ERROR: Invalid access to property or key 'position' on a base object of type 'previously freed'.
   at: _ready (res://answers/invalid-access-to-property-or-key-on-previously-freed/repro.gd:9)
   GDScript backtrace (most recent call first):
       [0] _ready (res://answers/invalid-access-to-property-or-key-on-previously-freed/repro.gd:9)

Fix it

Check the reference with is_instance_valid() before using it, and set the variable to null where you free the node, so later code sees an empty variable instead of a dead reference.

extends Node

var enemy: Node2D

func _ready() -> void:
	enemy = Node2D.new()
	add_child(enemy)
	enemy.free()
	print("is_instance_valid(enemy): ", is_instance_valid(enemy))
	print("enemy == null: ", enemy == null)
	if is_instance_valid(enemy):
		print(enemy.position)
	else:
		print("enemy was freed, skipping it")
	enemy = null
	print("after")

What the fixed script prints

Godot Engine v4.7.2.stable.mono.official.ed1daf0bf - https://godotengine.org

is_instance_valid(enemy): false
enemy == null: true
enemy was freed, skipping it
after

A signal or timer fires after the node is gone

A callback connected to something that outlives the node, such as a Timer elsewhere in the scene or an autoload's signal, can run after the node was freed and read it through a stored reference. Disconnect the signal when the node leaves the tree, or check is_instance_valid() at the top of the callback.

A list still holds freed nodes

An Array of enemies keeps its references after each enemy calls queue_free() on itself. Remove the node from the array when you free it, or filter the array with is_instance_valid before looping over it.

The same error in earlier Godot versions

Godot 4.0 to 4.2 printed it as: Invalid get index 'position' (on base: 'previously freed'). The cause and the fix are the same.

Verified on Godot 4.7.2

Both scripts on this page were run headless on Godot 4.7.2 (4.7.2.stable.mono.official.ed1daf0bf) on 6 October 2026, and the output above is what Godot printed, unedited.

Where the check lives in Godot's source: [gdscript_vm.cpp line 1281, the message](https://github.com/godotengine/godot/blob/4.7.2-stable/modules/gdscript/gdscript_vm.cpp#L1281), [gdscript_vm.cpp lines 62 to 72, where 'previously freed' is named](https://github.com/godotengine/godot/blob/4.7.2-stable/modules/gdscript/gdscript_vm.cpp#L62-L72).

Questions

Why does the error say 'previously freed' and not 'Nil'?
Freeing a node does not reach the variables that refer to it. They keep the reference to the freed object until you assign them again, and Godot names that case 'previously freed'.
Does enemy == null catch a freed node?
On Godot 4.7.2 it does: the fixed run on this page prints true for enemy == null after free(), and false for is_instance_valid(enemy). is_instance_valid() is the check that says what you mean, and it also covers a reference that was never assigned.