Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET: Failed to unload assemblies. Please check <this issue> for more information. #78513

Open
RedworkDE opened this issue Jun 21, 2023 · 103 comments

Comments

@RedworkDE
Copy link
Member

RedworkDE commented Jun 21, 2023

Godot version

Any 4.x version

Issue description

Assembly reloading can fail for various reasons, usually because a library used in tools code is not compatible with assembly unloading.

After unloading has failed, C# scripts will be unavailable until the editor is restarted (in rare cases it may be possible to complete the unloading by re-building assemblies after some time).

If assembly unloading fails for your project check Microsoft's troubleshooting instructions and ensure that you are not using one of the libraries known to be incompatible:

If you know of additional libraries that cause issues, please leave a comment.
If your code doesn't use any libraries, doesn't violate any guidelines and you believe unloading is blocked by godot, please open a new issue. Already reported causes are:

Minimal reproduction project & Cleanup example

using Godot;
using System;

[Tool]
public partial class UnloadingIssuesSample : Node
{
    public override void _Ready()
    {
        // block unloading with a strong handle
        var handle = System.Runtime.InteropServices.GCHandle.Alloc(this);

        // register cleanup code to prevent unloading issues
        System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(System.Reflection.Assembly.GetExecutingAssembly()).Unloading += alc =>
        {
            // handle.Free();
        };
    }
}

Footnotes

  1. Bugsquad edit 2

@wp2000x
Copy link

wp2000x commented Jul 6, 2023

Something was wrong with one of my [Tool] class in code after upgrade from 4.0 to 4.1 ("This class does not inherit from Node") and got this error. I just changed it to Node3D and back, and then the "cache" bug got fixed magicly?

@Quinn-L
Copy link

Quinn-L commented Jul 7, 2023

System.Text.Json also has this issue where serializing your classes will be held internally by the Json library.

The workaround for this using this library is also copied below:

var assembly = typeof(JsonSerializerOptions).Assembly;
var updateHandlerType = assembly.GetType("System.Text.Json.JsonSerializerOptionsUpdateHandler");
var clearCacheMethod = updateHandlerType?.GetMethod("ClearCache", BindingFlags.Static | BindingFlags.Public);
clearCacheMethod?.Invoke(null, new object?[] { null }); 

As far as I'm aware Godot doesn't provide an event for signalling when your dll/plugin is about to be unloaded, so you essentially need to call the above solution after every serialization/deserialization.

@RedworkDE
Copy link
Member Author

RedworkDE commented Jul 7, 2023

As far as I'm aware Godot doesn't provide an event for signalling when your dll/plugin is about to be unloaded, so you essentially need to call the above solution after every serialization/deserialization.

You can use the normal AssemblyLoadContext.Unloading event to trigger such cleanup code. I unfolded the code example in the initial post demonstrating its usage.

@Quinn-L
Copy link

Quinn-L commented Jul 7, 2023

You can use the normal AssemblyLoadContext.Unloading event to trigger such cleanup code. I unfolded the code example in the initial post demonstrating its usage.

Thanks for the info, I somehow missed that.

As an FYI, I played around with your solution trying it on a EditorPlugin derived class, but didn't find it reliable in on _Ready. As far as I could tell (or maybe I tested it wrong), rebuilding would reload the C# project but not re-invoke _Ready causing the error on subsequent rebuilds since the 'new' Unloading event is not registered (I assume because the node itself is not removed and re-added).

My solution was to place the code within a [ModuleInitializer] method, ie:

internal class AppModule
{
    [System.Runtime.CompilerServices.ModuleInitializer]
    public static void Initialize()
    {
        System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(System.Reflection.Assembly.GetExecutingAssembly()).Unloading += alc =>
        {
            var assembly = typeof(JsonSerializerOptions).Assembly;
            var updateHandlerType = assembly.GetType("System.Text.Json.JsonSerializerOptionsUpdateHandler");
            var clearCacheMethod = updateHandlerType?.GetMethod("ClearCache", BindingFlags.Static | BindingFlags.Public);
            clearCacheMethod?.Invoke(null, new object?[] { null });

            // Unload any other unloadable references
        };
    }
}

This ensures it is not dependent on any node(s) and is always registered only once, and re-registered upon reloading the assembly.

@taylorhadden
Copy link

I am not using Json.NET or System.Text.Json, and I am experiencing this issue. In the worst case, it results in data loss as serialized properties from C# Node scripts are reset to default.

The only library I am referencing is one I have authored. My .csproj file looks like this:

<Project Sdk="Godot.NET.Sdk/4.1.0">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <EnableDynamicLoading>true</EnableDynamicLoading>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\Lib\Hawthorn\Hawthorn\Hawthorn.csproj" />
  </ItemGroup>
</Project>

That project's csproj is dead simple, with no other project references.

@RedworkDE, you mentioned "does not violate any guidelines"; what are these guidelines? Is there a documentation page? Something else? Thanks.

@RedworkDE
Copy link
Member Author

It refers to Microsoft's docs page that was linked a bit further up: https://learn.microsoft.com/en-us/dotnet/standard/assembly/unloadability#troubleshoot-unloadability-issues

Also all these really also have to apply to the library, but for most libraries there isn't too much you can do about it (except not use the library)

@taylorhadden
Copy link

I am hitting this issue and I'm fairly confident it is not my (direct) fault.

I can reliably cause this error to appear in the console by running a C# rebuild while an offending Scene is loaded in the editor:

modules/mono/glue/runtime_interop.cpp:1324 - System.ArgumentException: An item with the same key has already been added. Key: BehaviorActorView`1[LeshonkiView]
     at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
     at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
     at Godot.Bridge.ScriptManagerBridge.ScriptTypeBiMap.Add(IntPtr scriptPtr, Type scriptType) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs:line 23
     at Godot.Bridge.ScriptManagerBridge.TryReloadRegisteredScriptWithClass(IntPtr scriptPtr) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs:line 579

If I build again, I will get the "Failed to unload assemblies" error. If I close the offending scene and run the rebuild again, I rebuild without any issues and everything is fine.

I've been trying to figure out precisely what's needed to get this problem to repro. It seems connected to the new [GlobalClass] attribute and resources, but not necessarily on a one-to-one basis.

@RedworkDE
Copy link
Member Author

You also seem to be using generic Nodes which also cause issues in some cases, see #79007

@taylorhadden
Copy link

taylorhadden commented Jul 15, 2023

Yeah, it seems that's the root cause. That would be very unfortunate if true. I have a micro reproduction project and I'll write up another issue. It runs fine but for the occasional compilation issue.

@taylorhadden
Copy link

I have created that reproduction project: #79519

@Pilvinen
Copy link

I don't know what is going on or why but I started getting this error today in Godot 4.1.1 .NET version. It's completely blocking me from doing any development. I've spent all day trying to hunt down weird behaviors. It seems that when this issue happens it's corrupting properties in the editor - or possibly inherited scenes are breaking.
Restarting the editor is good for one run. But I keep constantly having to restart.
This is 100% blocking all game development for me.

@Pilvinen
Copy link

Pilvinen commented Jul 23, 2023

Slight update. I'm just guessing here, but I suspect the issue might be somehow related to the new GlobalClass. That's all I can think of. I got rid of the errors by deleting a node which had either been added via GlobalClass node or it was a regular node with the same script attached to it. Either way, I deleted it and the error was gone. I added it back in as GlobalClass node - the error stayed gone.
Maybe this is helpful or maybe it is not, but I thought I'd mention it. It was a nightmare to track down.

@swissdude15
Copy link

I can absolutely understand that. The problems since 4.1 are unfortunately unexpected, varied and sometimes difficult to analyse. The new GlobalClass attribute does not work correctly for me so far, so I will not use this great feature for the time being. But I still got the error message above.

As described above, some problems are related to tool code whose assemblies cannot be unloaded during build. This certainly has side effects on GlobalClass classes. In principle, this is a problem of .net and poorly implemented libs (which cannot be unloaded) and not an engine bug. But it is important that a solution is found, because it makes the use of .net, especially as tool-code in the editor, much more difficult.

The simplest workaround is probably to close the problematic scene tabs (mostly [tool] code) before each build. Of course, this is annoying and tedious.

Otherwise, try the code listed at the top. Surprisingly, the following worked for me:

`[Tool]
...
            AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).Unloading += alc =>
            {

               // trigger unload 
                AssemblyLoadContext.GetLoadContext(typeof(JsonSerializerOptions).Assembly).Unload();
                AssemblyLoadContext.GetLoadContext(typeof(DataContractJsonSerializer).Assembly).Unload();

                // Unload any other unloadable references					

            };   

With this code, the unload works after an incorrect attempt.

@Ryan-000
Copy link
Contributor

Yeah, it seems that's the root cause. That would be very unfortunate if true. I have a micro reproduction project and I'll write up another issue. It runs fine but for the occasional compilation issue.

I added generic nodes today, and I started getting this issue.

@ashelleyPurdue
Copy link

I'm getting this issue in 4.1, and I'm not using any third-party libraries, nor am I using generic nodes, nor am I using the [GlobalClass] or [Tool] attributes. It just seems to be happening at random whenever I recompile.

Well, OK. I used to have a C# script that had both of those attributes, but I've since rewritten it in GDScript, so it shouldn't be relevant anymore...right? Is there any chance that the ghost of the C# version of that script still lives on? Maybe in some cache somewhere?

@Ryan-000
Copy link
Contributor

Ryan-000 commented Jul 29, 2023

Also happens whenever I enable a C# editor plugin, and only goes away once I disable the plugin and restart the editor.

@swissdude15
Copy link

The new [GlobalClass] attribute also causes this problem because these classes make it act like [Tool] code. The basic problem probably lies in the System.Collection.Generic class. :

  modules/mono/glue/runtime_interop.cpp:1324 - System.ArgumentException: An item with the same key has already been added. Key: Game.WeaponAbilityData
     at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
     at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
     at Godot.Bridge.ScriptManagerBridge.ScriptTypeBiMap.Add(IntPtr scriptPtr, Type scriptType) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs:line 23
     at Godot.Bridge.ScriptManagerBridge.AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs:line 419

I will try today the method mentioned above by Quinn-L and put the unload handler in an EditorPlugin. But I have rather little hope that it will work cleanly.

@swissdude15
Copy link

I will try today the method mentioned above by Quinn-L and put the unload handler in an EditorPlugin. But I have rather little hope that it will work cleanly.

Nope. We decided not to waste so much time with this problem and rewrite certain parts of the code in GDScript to work around the trouble spots with C#. We are not happy with this. This will especially affect [GlobalClass] and [Tool] code, probably also System.Collection.Generic will cause trouble.

@ashelleyPurdue
Copy link

ashelleyPurdue commented Aug 12, 2023

I think I figured out what was causing it for me. It wasn't editor plugins, global classes, or tool scripts. No, it's because I had more than one Node class in a single script file.

namespace FlightSpeedway
{
    public partial class Player : CharacterBody3D
    {
        private PlayerState _currentState;
        private Vector3 _spawnPoint;
        private Vector3 _spawnRotation;

        public void ChangeState<TState>() where TState : PlayerState
        {
            GD.Print($"Changing state to {typeof(TState).Name}");
            _currentState?.OnStateExited();

            foreach (var state in States())
            {
                state.ProcessMode = ProcessModeEnum.Disabled;
            }

            _currentState = States().First(s => s is TState);
            _currentState.ProcessMode = ProcessModeEnum.Inherit;
            _currentState.OnStateEntered();
        }

        private IEnumerable<PlayerState> States()
        {
            for (int i = 0; i < GetChildCount(); i++)
            {
                var child = GetChild<Node>(i);

                if (child is PlayerState state)
                    yield return state;
            }
        }
    }

    public partial class PlayerState : Node
    {
        protected Player _player => GetParent<Player>();
        protected Node3D _model => GetNode<Node3D>("%Model");

        public virtual void OnStateEntered() {}
        public virtual void OnStateExited() {}
    }
}

Once I moved PlayerState to a separate file, the issue stopped happening.

In case this is relevant, there are other node classes(such as PlayerFlyState) that inherit PlayerState. Those nodes exist as children of the player node, and they get enabled/disabled as the player changes states.

@ashelleyPurdue
Copy link

ashelleyPurdue commented Aug 12, 2023

I will try today the method mentioned above by Quinn-L and put the unload handler in an EditorPlugin. But I have rather little hope that it will work cleanly.

Nope. We decided not to waste so much time with this problem and rewrite certain parts of the code in GDScript to work around the trouble spots with C#. We are not happy with this. This will especially affect [GlobalClass] and [Tool] code, probably also System.Collection.Generic will cause trouble.

System.Collections.Generic definitely does not cause trouble. I've been using it extensively in a different Godot 4.1 project, and have never seen this problem in that project.

@swissdude15
Copy link

No, it's because I had more than one Node class in a single script file.

I can't confirm, we have all classes in separate files and as soon as [Tool] or [GlobalClass] is used, there are the problems described above. With [Tool] code you can work around the problem by closing these scene tabs before compiling. With [GlobalClass] you can't, because the editor keeps instances of these classes persistently in the background and the binding of some libs prevents unloading. And as I see it, System.Collection.Generic is used by the Godot code generator and this might cause trouble, because maybe one of the libraries in this namespace can't be unloaded correctly. I.e. even if you don't use a Generic in your [GlobalClass] script, it causes problems in the editor. At least this is always the case with us.

@ashelleyPurdue
Copy link

ashelleyPurdue commented Aug 12, 2023

No, it's because I had more than one Node class in a single script file.

I can't confirm, we have all classes in separate files and as soon as [Tool] or [GlobalClass] is used, there are the problems described above.

I'm not saying those scripts don't trigger the problem; I'm saying that multiple nodes in one file also triggers this problem.

@taylorhadden
Copy link

There are multiple causes of this problem. From the comments, there are at least three ways you can run into the issue..

@swissdude15

This comment was marked as off-topic.

@Rylius
Copy link

Rylius commented Sep 7, 2023

Just ran into this issue but in a seemingly different way than described above.

Failing code:

[GlobalClass]
public partial class WeaponDefinition : Resource
{
    [Export]
    public InventoryItemDefinition InventoryItem { get; private set; } = new();
}

InventoryItemDefinition is another GlobalClass resource. Figured I'd initialize the property with new() but that is a really bad idea. Think this causes some issues in the resource editor too, in addition to this issue.

Fixed code:

[GlobalClass]
public partial class WeaponDefinition : Resource
{
    [Export]
    public InventoryItemDefinition InventoryItem { get; private set; } = null!;
}

@Delsin-Yu
Copy link
Contributor

Is there a flow chart or design diagram for how any of the current C# implementation works? Juan says there is a new .NET implementation coming but there is no ETA. I wouldn't mind potentially digging into this if someone with some knowledge on it wants to help. I don't have time to reverse engineer it.

In the interim, I'm bypassing the EmitChange call in the custom resources and have hooked up a normal C# event instead. Going to run more testing to see if I hit the issue with this change. If not, we can at least assume the native callback implementation is a good place to start poking around.

I would not choose to do anything complex in an [Export]ed property personally, shenanigans could happen when you talking to the native side (any method call other than EmitChanged() that invokes the native side) in the middle of property deserialization.

@paulloz
Copy link
Member

paulloz commented Apr 11, 2024

Can we please recentre on topic please? This thread is already very long, the future GDExtension implementation is unrelated to the issue at hand here.

FWIW, the error you're experiencing is due to the deserialization of callables happening late after the state of your objects is restored. I have already started to look at how we could fix those timing issues during our assembly reload process last week. I need to run more tests on exactly how to improve this without changing the current behaviour.

@ColaMint
Copy link

This issue has been bothering me for a long time. Today I found the root cause through continuous attempts. It is caused by the use of anonymous functions when binding signals!

@Delsin-Yu
Copy link
Contributor

This issue has been bothering me for a long time. Today I found the root cause through continuous attempts. It is caused by the use of anonymous functions when binding signals!

This issue has fixed in 4.3 you may try it out if the fix works.
#83217

@AThousandShips
Copy link
Member

AThousandShips commented Apr 14, 2024

It's not in 4.3.dev5, it will be in dev6 which should release in a few days

@Delsin-Yu
Copy link
Contributor

It's not in 4.3.dev5, it will be in dev6 which should release in a few days

I compiled the latest godot from the master branch, but I still encounter this problem. I can only solve it by binding the event this way: xxxx.Connect(SignalName.Pressed, new Callable(this, MethodName.XXXX)

  1. Do not use += to bind events
  2. Do not use anonymous functions

If possible, can you publish an issue about this, and, include a MRP (minimal reproducible project) for this issue you are encountering? Or, just share the code snippet that causes the issue?

@ColaMint
Copy link

ColaMint commented Apr 15, 2024

It's not in 4.3.dev5, it will be in dev6 which should release in a few days

I compiled the latest godot from the master branch, but I still encounter this problem. I can only solve it by binding the event this way: xxxx.Connect(SignalName.Pressed, new Callable(this, MethodName.XXXX)

  1. Do not use += to bind events
  2. Do not use anonymous functions

If possible, can you publish an issue about this, and, include a MRP (minimal reproducible project) for this issue you are encountering? Or, just share the code snippet that causes the issue?

I'm very sorry, I found that I didn't compile the mono version of godot through the correct process. At present, I don't know how to compile GodotSharp, so I directly copied the old GodotSharp folder and used it with the new godot binary, so my test results is not credible.

I will try again when 4.3-dev6 is officially released.

This is my test code:

using Godot;

[Tool]
public partial class Main : GridContainer
{
        public override void _Ready()
        {

                for (int i = 0; i < 9; ++i)
                {
                        Button button = new()
                        {
                                Text = i.ToString(),
                        };

                        int id = i;
                        button.Pressed += () =>
                        {
                                OnButtonPressed(id);
                        };

                        AddChild(button);
                }
        }

        private void OnButtonPressed(int id)
        {
                GD.Print($"Button {id}");
        }
}

My testing process is:

  1. Start godot and open the main scene, making sure that the editor renders the content of the scene.
  2. Start vscode, modify a little bit of code, and click the run button in godot editor to compile and run the code.

@Delsin-Yu
Copy link
Contributor

It's not in 4.3.dev5, it will be in dev6 which should release in a few days

I compiled the latest godot from the master branch, but I still encounter this problem. I can only solve it by binding the event this way: xxxx.Connect(SignalName.Pressed, new Callable(this, MethodName.XXXX)

  1. Do not use += to bind events
  2. Do not use anonymous functions

If possible, can you publish an issue about this, and, include a MRP (minimal reproducible project) for this issue you are encountering? Or, just share the code snippet that causes the issue?

I'm very sorry, I found that I didn't compile the mono version of godot through the correct process. At present, I don't know how to compile GodotSharp, so I directly copied the old GodotSharp folder and used it with the new godot binary, so my test results is not credible.

I will try again when 4.3-dev6 is officially released.

This is my test code:

using Godot;

[Tool]
public partial class Main : GridContainer
{
        public override void _Ready()
        {

                for (int i = 0; i < 9; ++i)
                {
                        Button button = new()
                        {
                                Text = i.ToString(),
                        };

                        int id = i;
                        button.Pressed += () =>
                        {
                                OnButtonPressed(id);
                        };

                        AddChild(button);
                }
        }

        private void OnButtonPressed(int id)
        {
                GD.Print($"Button {id}");
        }
}

My testing process is:

  1. Start godot and open the main scene, making sure that the editor renders the content of the scene.
  2. Start vscode, modify a little bit of code, and click the run button in godot editor to compile and run the code.

See the .Net specific page for Compiling the Engine.

@ColaMint
Copy link

It's not in 4.3.dev5, it will be in dev6 which should release in a few days

I compiled the latest godot from the master branch, but I still encounter this problem. I can only solve it by binding the event this way: xxxx.Connect(SignalName.Pressed, new Callable(this, MethodName.XXXX)

  1. Do not use += to bind events
  2. Do not use anonymous functions

If possible, can you publish an issue about this, and, include a MRP (minimal reproducible project) for this issue you are encountering? Or, just share the code snippet that causes the issue?

I'm very sorry, I found that I didn't compile the mono version of godot through the correct process. At present, I don't know how to compile GodotSharp, so I directly copied the old GodotSharp folder and used it with the new godot binary, so my test results is not credible.
I will try again when 4.3-dev6 is officially released.
This is my test code:

using Godot;

[Tool]
public partial class Main : GridContainer
{
        public override void _Ready()
        {

                for (int i = 0; i < 9; ++i)
                {
                        Button button = new()
                        {
                                Text = i.ToString(),
                        };

                        int id = i;
                        button.Pressed += () =>
                        {
                                OnButtonPressed(id);
                        };

                        AddChild(button);
                }
        }

        private void OnButtonPressed(int id)
        {
                GD.Print($"Button {id}");
        }
}

My testing process is:

  1. Start godot and open the main scene, making sure that the editor renders the content of the scene.
  2. Start vscode, modify a little bit of code, and click the run button in godot editor to compile and run the code.

See the .Net specific page for Compiling the Engine.

Thank you! I can confirm that the new version has indeed fixed this issue.

@TandersT
Copy link

I encountered this error regularly in 4.2.2 as well. Running Godot Engine v4.3.dev.mono.custom_build.7529c0bec I no longer encounter the issue on any normal script, but I have a editor plugin that fails almost instantly as I run the project

@paulloz
Copy link
Member

paulloz commented Apr 22, 2024

As stated in the original message: if you're encountering this error, think the issue is caused by Godot, and does not fall in the list of already reported causes (the list up top is edited and maintained up to date), please open a dedicated issue. Thank you.

@Tichau
Copy link

Tichau commented Apr 24, 2024

Hello, I encountered the issue by adding a static constructor on a node class (used to initialize a static variable to avoid having to declare it as a nullable). I initialized it in the node constructor and ignored the warning CS8618 as a workaround.

@Delsin-Yu
Copy link
Contributor

@Tichau If possible, can you publish an issue about this, and, include a MRP (minimal reproducible project) for this issue you are encountering? Or, just share the code snippet that causes the issue?

@DevMimas
Copy link

Hello, I received the same error message in version 4.2.2. When changing an EditorPlugin written in C#. More precisely, when a script was attached to the [Tool] attribute to use it in the EditorPlugin.

Godot Engine v4.2.2.stable.mono.official (c) 2007-present Juan Linietsky, Ariel Manzur & Godot Contributors.

modules/mono/mono_gd/gd_mono.cpp:529 - .NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information. (User)
modules/mono/mono_gd/gd_mono.cpp:529 - .NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information. (User)
modules/mono/mono_gd/gd_mono.cpp:529 - .NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information. (User)
modules/mono/mono_gd/gd_mono.cpp:513 - .NET: Giving up on assembly reloading. Please restart the editor if unloading was failing. (User)

Good idea to write this in an error message.

@sebialex
Copy link

sebialex commented Jun 12, 2024

I thought I was done with these kind of issues (4.3.dev6 looked like it was good), but I'm getting this after upgrading to 4.3.beta1 (Windows 10, .NET).

 Assertion failed: Script path can't be empty.
    Details: 
     at Godot.GodotTraceListener.Fail(String message, String detailMessage) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs:line 24
     at System.Diagnostics.TraceInternal.Fail(String message, String detailMessage)
     at System.Diagnostics.Debug.Fail(String message, String detailMessage)
     at Godot.Bridge.ScriptManagerBridge.GetGlobalClassName(godot_string* scriptPath, godot_string* outBaseType, godot_string* outIconPath, godot_string* outClassName) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs:line 232

No idea which of my source files is causing this, it doesn't say. I'm not sure if this is related, but it feels like it.

@Delsin-Yu
Copy link
Contributor

Hello, I received the same error message in version 4.2.2. When changing an EditorPlugin written in C#. More precisely, when a script was attached to the [Tool] attribute to use it in the EditorPlugin.

Godot Engine v4.2.2.stable.mono.official (c) 2007-present Juan Linietsky, Ariel Manzur & Godot Contributors.

modules/mono/mono_gd/gd_mono.cpp:529 - .NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information. (User)
modules/mono/mono_gd/gd_mono.cpp:529 - .NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information. (User)
modules/mono/mono_gd/gd_mono.cpp:529 - .NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information. (User)
modules/mono/mono_gd/gd_mono.cpp:513 - .NET: Giving up on assembly reloading. Please restart the editor if unloading was failing. (User)

Good idea to write this in an error message.

#78513 (comment)
#78513 (comment)
https://learn.microsoft.com/en-us/dotnet/standard/assembly/unloadability#troubleshoot-unloadability-issues

@hhyyrylainen
Copy link

Does anyone of those links explain the behaviour that the assembly unload stops working after a couple of reloads? I'm still on 4.2.2 but that's what I'm seeing: assembly reloading works a couple of times usually after which it stops working. I don't think any new code from my side that uses any of the things that are said to cause assembly unloading issues would run only after like the seconds or third time an assembly reload happens.

@Delsin-Yu
Copy link
Contributor

Delsin-Yu commented Jun 12, 2024

Does anyone of those links explain the behaviour that the assembly unload stops working after a couple of reloads? I'm still on 4.2.2 but that's what I'm seeing: assembly reloading works a couple of times usually after which it stops working. I don't think any new code from my side that uses any of the things that are said to cause assembly unloading issues would run only after like the seconds or third time an assembly reload happens.

4.2.x is suffering from all four major issues. These are bugs within the Godot Engine / .Net Module that a user will encounter regardless of whether they are creating Editor Tools or not. These issues are fixed in 4.3 but not (yet?) cherry-picked to 4.2 (or maybe never for #81903 because of this).
#79519
#81903
#80175
#90837
But things are different for EditorPlugins. Failed unloading can happen by user error, especially during editor script development where exceptions can break unloading functionalities, or as the link explains, certain libraries or use cases are not unloadable by C# design, there is nothing for us language consumer can do other than rebooting the editor.

@Odaimoko
Copy link

Odaimoko commented Aug 10, 2024

I encounter this after compiling/running scene with another godot instance, mostly from command line after compiling new csharp scripts (IDE: Jetbrains Rider).

godot --path ".../my project path" "res://Scenes/GameMain.tscn"

And then I go back to the editor, it pops up this thread.

@maxime4000
Copy link

I would like to share my Theory.

TL;DR; Multiple Resource classes that are "GlobalClass" and located in the same file are problematic and when you refactor all your resource into separated files, this issue disappear.

The long version:

I was having this issues through 4.2 until 4.3-RC1. Just before RC1 release, I had refactor a lot of my code and split files from multiple classes inside a single file to each class has it's own separated file. When RC1 release, I just finished that refactor and I was happy that the Engine was now working. I've though the issue was solve within RC1, while it wasn't mention though the changelog.

RC1, RC2, RC3 and 4.3-stable were all improving my experience until I've start designing a new systems for my game. This issues that was uncleared to me but only allowed me to build my project once per "Godot execution" was back and worst. Then I though maybe I have found the culprit.

Right now I can't confirm but I highly think I've found a reproductible pattern. When I had the issue, I had multiple Resource classes found in the same file at multiple location.

I had this issues where my scenes where corrupted after running into this issues and saving my scene. (AKA: Running your game will save any opened and edited scene which were edited not by me, but by the compilation issue process. Resulting that after any build that has failed, the following build will corrupt your opened scene if this issues has happen. the C# meta data is corrupted and Godot will try using this new meta data, will find any node with a "Resource" [Export] property to be incorrect and will corrupt it with a null value...). All my tscn files changed and now having exported variable inserted to null instead of what it was before.

Signal were corrupted and to have my build working again, I had to "reset" each exported variable that was corrupted. I've start connecting signals through my C# code due to that issues...

But one of the things I notice is that all my "resource" exported variable where the Script value in the Inspector was "corrupted". (By memory) It look like: "res://src/SomethingComponent.cs::Resource_4go2v" the UUID at the end. I had to manually connect the Script to the Right C# file to fix the issues and it was reccuring like "Moe's kicking Barney out of his bar" meme.

One thing I made sure to fix in the refactoring I have done prior to RC1 was that I had one file with like 10 Resource "GlobalClass" and one day I've decide to refactor all those classes into separated file for each Resource class. After fixing my .tres files, it didn't happen for like 3 weeks. Until I start working on my next systems, which I guess I start creating new Resource classes and put multiple resource classes inside the same file again. Now I am stuck with this issues which disappear for a short period.

Pretty sure it's related to something like this. When I will tackle the refactoring, I will make sure to do a follow up about this theory. Until then, I encourage anybody to test this solution if it solved the issue. At least, if it doesn't solve the issues, it will reduced the Tech Debt in their game. Lol

Those are example of visual distinction found when the bug happen.

image
image

@hhyyrylainen
Copy link

hhyyrylainen commented Aug 26, 2024

I don't think I've used any Godot resources at all. Other than the built-in ones of course. So I think my project contains zero resources with a C# script related to them. And I still see this issue (though I haven't updated to 4.3 yet as I've been busy the past week) so I don't think that can be the whole picture.

Edit: I've now tried Godot 4.3 and I still got the same issue within about 5 minutes.

@DSOE1024
Copy link

DSOE1024 commented Sep 5, 2024

Is it possible that the resourceformatsaver and resourceformatloader that you wrote yourself could cause this problem?

@Delsin-Yu
Copy link
Contributor

Delsin-Yu commented Oct 11, 2024

@maxime4000 If possible, can you try if #98097 fixes your issue? No, I will continue to search for the reason

Update

@MDuRieu
Copy link

MDuRieu commented Oct 18, 2024

I had this error caused by using [Export] on an enum and assigning it to the MultiplayerSynchroniser. Not sure which part was the exact cause.

@hunterloftis
Copy link

Multiple Resource classes that are "GlobalClass" and located in the same file are problematic and when you refactor all your resource into separated files, this issue disappear.

This is not the case, at least not universally. I'm consistently seeing this issue in a project in which there are no shared GlobalClass files.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests