godot: void Godot.Object.Finalize(): FATAL: Condition "!Object::cast_to(p_ptr)" is true

Godot version

Godot Engine v4.0.beta3.mono.official.01ae26d31

System information

Windows11 Vulkan API 1.2.0 - Using Vulkan Device #0: NVIDIA - NVIDIA GeForce GTX 1080

Issue description

error info:

E 0:00:15:0082   void Godot.Object.Finalize(): FATAL: Condition "!Object::cast_to<RefCounted>(p_ptr)" is true.
  <C++ 源文件>      modules/mono/glue/runtime_interop.cpp:155 @ godotsharp_internal_refcounted_disposed()
  <栈追踪>          Godot.NativeInterop.NativeFuncs.generated.cs:88 @ void Godot.NativeInterop.NativeFuncs.godotsharp_internal_refcounted_disposed(IntPtr , IntPtr , Godot.NativeInterop.godot_bool )()
                 Object.base.cs:117 @ void Godot.Object.Dispose(Boolean )()
                 Object.base.cs:80 @ void Godot.Object.Finalize()()

Hi, my english is poor. I don’t know how to solve this problem, but it is not a problem in version 3.5.1. Looks like a GC problem This error occur about 2 seconds after the EmitSignal function call, Then the program will crash

            if (shouldEmitSignal)
            {
                var inventoryEvent = InventoryEvent.NewAddEvent(inventoryItem);
                EmitSignal(SignalName.OnEvent, inventoryEvent);
            }

Steps to reproduce

This is my code


            if (shouldEmitSignal)
            {
                var inventoryEvent = InventoryEvent.NewAddEvent(inventoryItem);
                EmitSignal(SignalName.OnEvent, inventoryEvent);
            }

InventoryEvent

    public enum InventoryEventType
    {
        Unknown,
        Add, 
        Remove 
    }

    public partial class InventoryEvent : Godot.RefCounted
    {
        public InventoryEventType type;
        public InventoryItem inventoryItem;

        public InventoryEvent()
        {

        }

        public static InventoryEvent NewAddEvent(InventoryItem inventoryItem)
        {
            InventoryEvent inventoryEvent = new InventoryEvent();
            inventoryEvent.type = InventoryEventType.Add;
            inventoryEvent.inventoryItem = inventoryItem;
            return inventoryEvent;
        }

        public static InventoryEvent NewRemoveEvent(InventoryItem inventoryItem)
        {
            InventoryEvent inventoryEvent = new InventoryEvent();
            inventoryEvent.type = InventoryEventType.Remove;
            inventoryEvent.inventoryItem = inventoryItem;
            return inventoryEvent;
        }
}

InventoryItem

    public partial class InventoryItem : Godot.RefCounted
    {
        [JsonProperty]
        public string icon;
        [JsonProperty]
        public Level level;
        [JsonProperty]
        public InventoryItemType type;
        [JsonProperty]
        public EquipType equipType;
        [JsonProperty]
        public QualityType qualityType;
       ......                  

Minimal reproduction project

XianRPG - Backup.zip

No response

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 18 (7 by maintainers)

Most upvoted comments

@firebelley Thanks! #69971 should solve the issue in your project.

And even more interesting, using the as keyword seems to return null after the cast:

var csFile = GD.Load<CSharpScript>($"res://TestDefinition.cs");

// Definition will be null
Definition = (object)csFile.New() as BaseDefinition;

Script.New returns a Variant. Here you’re boxing that and then attempting to cast it to BaseDefinition which is an invalid cast. Variant has a helper implicit conversion, which is the one you’re using in your first example: (BaseDefinition)csFile.New(); (shorthand for (BaseDefinition)csFile.New().AsGodotObject();).

Ok I was able to reproduce this, MRP here: RefCounted Crash MRP.zip

My use case is somewhat strange, so I’ve replicated the relevant parts of my use case in the MRP. Basically, when a CSharpScript is loaded using GD.Load and then instantiated, then the error will occur on garbage collect or on game exit depending on how the code is implemented. The error doesn’t appear to be related to signals. This code:

var csFile = GD.Load<CSharpScript>($"res://TestDefinition.cs");

// Assignment like this will error immediately on garbage collection
var def = (object)csFile.New();
Definition = (BaseDefinition)def;

Will cause an error immediately upon the next garbage collect. In the MRP I am calling GC.Collect after every time this script is instantiated. The expected behavior is that the value of csFile.New() can be cast to BaseDefinition, since TestDefinition extends BaseDefintion:

// TestDefinition.cs
public partial class TestDefinition : BaseDefinition
{
  public override string GetDefinition()
  {
    return "This is a test definition.";
  }
}

// BaseDefinition.cs
public abstract partial class BaseDefinition : RefCounted
{
  public abstract string GetDefinition();
}

Interestingly, if the cast is applied directly to csFile.New() the game will only crash with the error when closing the window:

var csFile = GD.Load<CSharpScript>($"res://TestDefinition.cs");

// Assignment like this will error when the game is closed
Definition = (BaseDefinition)csFile.New();

In the MRP you can see that when the above method of instantiating the script is used, then you can call GetDefinition on Definition and see a print to the console with the correct data. This obviously indicates that the cast was successful.

And even more interesting, using the as keyword seems to return null after the cast:

var csFile = GD.Load<CSharpScript>($"res://TestDefinition.cs");

// Definition will be null
Definition = (object)csFile.New() as BaseDefinition;

And to reiterate, the error in both cases is as follows:

ERROR: FATAL: Condition "!Object::cast_to<RefCounted>(p_ptr)" is true.
   at: godotsharp_internal_refcounted_disposed (modules/mono/glue/runtime_interop.cpp:155)
Fatal error. System.Runtime.InteropServices.SEHException (0x80004005): External component has thrown an exception.
   at Godot.NativeInterop.NativeFuncs.godotsharp_internal_refcounted_disposed(IntPtr, IntPtr, Godot.NativeInterop.godot)
   at Godot.Object.Dispose(Boolean)
   at Godot.Object.Finalize()