PackedVector2Array contains data but size is 0?

Godot Version

4.7.1 stable

Question

Code:

@tool

class_name SimpleArea
extends Node2D

signal position_changed(new_position)

@export var corners: PackedVector2Array = PackedVector2Array([Vector2(0, 0), Vector2(0, 0)])

func _set(property: StringName, value) -> bool:
	if property == "position":
		if !Engine.is_editor_hint():
			return true
		position_changed.emit(value)

	return true

func _ready() -> void:
	position_changed.connect(_on_position_changed)

func _on_position_changed(new_position: Vector2):
	print("pos_changed")
	print(corners)
	print("amount of elements: ", corners.size())
	if corners[0] == corners[1]:
		print("Equal Corners")
		corners = PackedVector2Array([new_position, new_position]

output:

pos_changed
[(0.0, 0.0), (0.0, 0.0)]
amount of elements: 0
  ERROR: res://addons/simple2dareaprovide/simple_area.gd:25 - Out of bounds get index '0' (on base: 'Array[Vector2]')
Set position

This works fine if I replace the PackedVector2Array with a Array[Vector2]
My best guess is that it somehow relates to this

Note: Packed arrays are always passed by reference. To get a copy of an array that can be
modified independently of the original array, use duplicate().

from the docs.

But at the same time I am not ever modifying the values. (At least not before the error)
If I run 'corners.duplicate()` I get a truly empty array.

That script does not replicate the behavior you describe. Is that all you’re actually doing?

It’s simply impossible that printing the content of an array shows there are 2 elements in it and on the next line printing its size shows 0.

You also said that it works when you use Array[Vector2] but the error message you posted indicates that the error is for Array[Vector2]

Something doesn’t add up in your problem report here.

Interesting.
Note that my script originally used Array[Vector2]. I then modified it to use PackedVector2Array
I used the same Node. (An unmodified SimpleArea Node)
I now deleted this Node and replaced it with an identical one. (Since you couldn’t reproduce the error I thought this might help.)
That solved the issue.

Not sure I understand what the “issue” was?

The issue was the behavior as described above.
when I printed out the array I could see its contents, but whenever I tried to access the data I got an out of bounds error. It was in a very weird state between beeing there and not beeing there.

Apparently this behavior was created by creating an Array[Vector2] and refactoring it to a PackedVector2Array.
I assume this is some kind of minor bug in the engine.

That’s simply not possible. Can you make a minimal example that reproduces it?

This code produces the error.
This version didnt work. See below for actually error producing code.

@tool

class_name PackedErrorProducer
extends Node2D

signal position_changed(Vector2)

## This tries to assign Array[Vector2] to a PackedVector2Array
## This is what creates the error
var array: PackedVector2Array = [Vector2(0, 0), Vector2(0, 0)]

## This entire logic is not required for the error
## But we need some way to check the value of array
## So I just do it whenever the position of the node is
## changed.
func _set(property: StringName, value) -> bool:
	if property == "position":
		if !Engine.is_editor_hint():
			return true
		position_changed.emit(value)

	return true

func _ready() -> void:
	position_changed.connect(_on_pos_changed)

func _on_pos_changed(_new_vector: Vector2):
	print(array)
	print(array.size())

However you can now later on refactor the code to

@tool

class_name PackedErrorProducer
extends Node2D

signal position_changed(Vector2)

## This is correct but the error persists for existing Nodes.
var array: PackedVector2Array = PackedVector2Array([Vector2(0, 0), Vector2(0, 0)])

## This entire logic is not required for the error
## But we need some way to check the value of array
## So I just do it whenever the position of the node is
## changed.
func _set(property: StringName, value) -> bool:
	if property == "position":
		if !Engine.is_editor_hint():
			return true
		position_changed.emit(value)

	return true

func _ready() -> void:
	position_changed.connect(_on_pos_changed)

func _on_pos_changed(_new_vector: Vector2):
	print(array)
	print(array.size())

For all existing Nodes using the script the error will persist

It does not produce the error.

It also doesn’t run in the form you posted it. There’s a missing parenthesis on the last line.

There’s no problem in assigning a plain array to a packed variable because packed array class has a constructor that takes a plain array as an argument.

My god you are right. I tried it again and this time it didnt work??? But when I coded it it did. I will try again to find out what exactly created it. I have a theory on what I did wrong give me a sec.

Could be that you deleted the contents of the exported array variable in the inspector. Although printing array’s content in that case would also show that the array is empty.

Ok so here is what you have to do.

Step 1:

Create this script:

@tool

class_name PackedErrorProducer
extends Node2D

signal position_changed(Vector2)

## This tries to assign Array[Vector2] to a PackedVector2Array
## This is what creates the error
@export var array: Array[Vector2] = [Vector2(0, 0), Vector2(0, 0)]

## This entire logic is not required for the error
## But we need some way to check the value of array
## So I just do it whenever the position of the node is
## changed.
func _set(property: StringName, value) -> bool:
	if property == "position":
		if !Engine.is_editor_hint():
			return true
		position_changed.emit(value)

	return true

func _ready() -> void:
	position_changed.connect(_on_pos_changed)

func _on_pos_changed(_new_vector: Vector2):
	print(array)
	print(array.size())

Step 2:

Create a 2D scene and add a PackederrorProducer as a child

Step 3:

change the Array[Vector2] to a PackedVectoryArray in the existing script

@tool

class_name PackedErrorProducer
extends Node2D

signal position_changed(Vector2)

## This tries to assign Array[Vector2] to a PackedVector2Array
## This is what creates the error
@export var array: PackedVector2Array = PackedVector2Array([Vector2(0, 0), Vector2(0, 0)])

## This entire logic is not required for the error
## But we need some way to check the value of array
## So I just do it whenever the position of the node is
## changed.
func _set(property: StringName, value) -> bool:
	if property == "position":
		if !Engine.is_editor_hint():
			return true
		position_changed.emit(value)

	return true

func _ready() -> void:
	position_changed.connect(_on_pos_changed)

func _on_pos_changed(_new_vector: Vector2):
	print(array)
	print(array.size())

The reason I had the error in my testing is that I originally did suspect it was the refactoring Array → PackedVector2Array (which was correct)
However then I had the theory that it was because during refactoring I had the line
var array: PackedVector2Array = [Vector2(0, 0), Vector2(0, 0)]
which looks like it might create errors. I then tested if I could reproduce the error only with that and I could. But that was only because stupid me forgot to delete the old Node and replace it with a new one. So obviously the old corrupted Array was still saved in it.
This is a really tricky to debug bug.

You need to reload the scene when doing interventions like this in tool scripts, so that changed variables are re-initialized properly.

Although it looks particularly odd that:

print(array)
print(array.size())

indeed outputs:

[(0.0, 0.0), (0.0, 0.0)]
0

This might be worth reporting as a bug.

curiously enough also

print(array[0]) # Out of bounds error
print(array.get(0)) # Works