godotengine / godotengine/godot

Loading PackedScene with inherited C# Resource via LoadThreadedRequest leaks RefCounted

Open
#122,001 2 comments 2 reactions 0 assignees View on GitHub
bug confirmed topic:core topic:dotnet
Dominant language
C++
Stars
117k
Forks
26.8k
PR merge metrics
PR metrics pending

Description

### Tested versions

Reproducible in:
- Godot Engine v4.7.1.stable.mono.official.a13da4feb
- Godot Engine v4.7.stable.mono

### System information

Windows 11 Pro - Forward+ - D3D12 - .NET / C# (v10.0.301)

### Issue description

Loading a `PackedScene` using the threaded `ResourceLoader` leaks one `RefCounted` instance when the scene contains an exported custom C# `Resource` that inherits from another custom C# `Resource`.

The leak only occurs when using:

```csharp
ResourceLoader.LoadThreadedRequest(...)
ResourceLoader.LoadThreadedGet(...)
```

The issue does **not** reproduce when:

- Loading the same scene using `GD.Load()`
- Using `ResourceLoader.Load()`
- Exporting a resource that derives directly from `Resource`
- Using built-in resources such as `Curve`

Running Godot with `--verbose` reports:

```text
Leaked instance: RefCounted: - Reference count: 0
```

### Steps to reproduce

Create the following resources:

```csharp
using Godot;

[GlobalClass]
public partial class BaseResource : Resource
{
}
```

```csharp
using Godot;

[GlobalClass]
public partial class DerivedResource : BaseResource
{
}
```

Create a scene with the following script:

```csharp
using Godot;

public partial class Empty : Node
{
[Export]
private DerivedResource _resource = null!;
}
```

Assign a built-in instance of `DerivedResource` in the Inspector.

Load the scene using the threaded loader:

```csharp
const string path = "res://empty.tscn";

var error = ResourceLoader.LoadThreadedRequest(path);

while (ResourceLoader.LoadThreadedGetStatus(path)
!= ResourceLoader.ThreadLoadStatus.Loaded)
{
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
}

var scene = ResourceLoader.LoadThreadedGet(path);

GD.Print(scene);
```

Close the application.

---

# Expected behavior

The application should exit without reporting leaked objects.

---

# Actual behavior

On shutdown Godot reports:

```text

WARNING: 1 ObjectDB instance was leaked at exit
(run with `--verbose` for details)

Leaked instance: RefCounted: - Reference count: 0
```

---

# Additional observations

The issue **does not** reproduce with a resource that derives directly from `Resource`:

```csharp
using Godot;

[GlobalClass]
public partial class DirectResource : Resource
{
}
```

```csharp
using Godot;

public partial class Empty : Node
{
[Export]
private DirectResource _resource = null!;
}
```

The issue also **does not** reproduce when using built-in resources, for example:

```csharp
using Godot;

public partial class Empty : Node
{
[Export]
private Curve _curve = null!;
}
```

Likewise, replacing the threaded loading code with:

```csharp
var scene = GD.Load("res://empty.tscn");
```

does **not** produce the leak.

I reduced this issue from a much larger project to this minimal reproduction. The project contains only the classes shown above and consistently reproduces the leak on both Godot 4.7 and 4.7.1 Mono.

### Minimal reproduction project (MRP)

[DerivedResourceBug.zip](https://github.com/user-attachments/files/30617032/DerivedResourceBug.zip)

Contributor guide

Open the contributing guide

Research direction

Start by running the attached DerivedResourceBug.zip minimal reproduction on the affected Mono versions with verbose logging. Compare the threaded ResourceLoader.LoadThreadedRequest/LoadThreadedGet path with the synchronous loading cases described in the issue. Done means the inherited C# Resource scene loads without an ObjectDB leak at shutdown while the documented non-reproducing cases remain unaffected.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, godot
Domain
game-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.