Unsubscribing from signal does not seem to work

Godot Version: 4.3 stable

Problem

I am having problems with HttpRequest signals in C#. I have been debugging my code but I started to think it is an engine bug. The code I am going to share is a little bit difficult to follow so I am going to try explaining it. I am not sure it is a good idea to simplify the code as I think the problem is related with object lifecycles.

I know I can easily rewrite my code, I am not looking for a workaround, I just want to find where the problem is, if it is something related with my code I apologize in advance. A C# debugger is recommended.

Well, I wanted to create an InternetChecker class, it is a standard class (not a Node) that I can call from a node. The idea is checking if the user has internet by making HTTP requests to some urls. The class has a Check method that receives a list of urls and a couple of callbacks, success and failure.

The Cehck method creates N HttpRequest nodes being N the number of urls we passed. Each HttpRequest is suposed to run in parallel so I subscribe to RequestCompleted on each one. The thing is: if any of the request succeeds I know that the user has internet so I can unsubscribe from the other HttpRequests and invoke the success listener.

This last part is the one failing, godot says that the signal I am trying to unsubsribe does not exist in one context but after that, in other context it works. (You need to read and test the code to see what I am trying to explain)

Actual class:

public class InternetChecker
{
	const int DEFAULT_REQUEST_TIMEOUT = 4;
	
	Node m_Parent;
	int m_RequestTimeout;

	List<HttpRequest> m_HTTPRequests;

	public InternetChecker(Node parent, int requestTimeout = DEFAULT_REQUEST_TIMEOUT)
	{
		m_Parent = parent;
		m_RequestTimeout = requestTimeout;
	}

	public void Check(string[] urls, Action onSuccessListener, Action onFailureListener)
	{
		m_HTTPRequests = new List<HttpRequest>(urls.Length); // Here I store the references to HttpRequest nodes
		int checks = 0;
		
		foreach (string url in urls)
		{
			// Create HttpRequest nodes
			HttpRequest httpRequest = new HttpRequest()
			{
				Timeout = m_RequestTimeout
			};

			m_Parent.CallDeferred("add_child", httpRequest); // This is done like that so I can call this from a node _Ready()
			m_HTTPRequests.Add(httpRequest);


			// Request Completed Listener
			void _OnRequestCompleted(long result, long responseCode, string[] headers, byte[] body)
			{
				// Here we unsuscribe from the signal and remove the http request node from the register
				GD.Print($"We have response from: {httpRequest.Name}, Unregistering: {httpRequest.Name}");
				httpRequest.RequestCompleted -= _OnRequestCompleted;
				m_HTTPRequests.Remove(httpRequest);
				// FreeHTTPRequest(httpRequest);

				
				// 0 -> SUCCESS
				// https://docs.godotengine.org/en/stable/classes/class_httprequest.html#enumerations
				if ( result == (int) HttpRequest.Result.Success)
				{
					// If one http request succeeds we have internet so we no longer care about other http respones so we want to unsubscribe from them
					for (int i = m_HTTPRequests.Count - 1; i >= 0; i--)
					{
						GD.Print($"Unregistering: {m_HTTPRequests[i].Name}");
						m_HTTPRequests[i].RequestCompleted -= _OnRequestCompleted; // THIS FAILS
						m_HTTPRequests.RemoveAt(i);
						// FreeHTTPRequest(m_HTTPRequests[i]);
					}
				
					onSuccessListener.Invoke();
				}
				else
				{
					// If the http request fails we just keep waiting until all have finished so if none succeded we don't have internet
					checks += 1;
				
					if (checks == urls.Length)
					{
						onFailureListener.Invoke();
					}
				}
			}

			void _OnHTTPRequestReady()
			{
				httpRequest.Ready -= _OnHTTPRequestReady;

				GD.Print($"Connecting {httpRequest.Name}");
			
				httpRequest.RequestCompleted += _OnRequestCompleted;
				httpRequest.Request(url);
			}

			// I need to waint until node is ready as I am creating the node with CallDeferred()
			httpRequest.Ready += _OnHTTPRequestReady;
		}
	}


	// References to this are commented so this never gets called to simplify a little bit
	void FreeHTTPRequest(HttpRequest httpRequest)
	{
		m_Parent.RemoveChild(httpRequest);
		httpRequest.QueueFree();
	}

}

How to use

Create a node and attach this script, make sure you have the InternetChecker class also

using Godot;

public partial class MyNode : Node
{

    public override void _Ready()
    {
		InternetChecker internetChecker = new InternetChecker( GetTree().Root );

		string[] urls = new string[] {"https://www.google.com/", "https://ismyinternetworking.com/"};
		internetChecker.Check(urls, OnInternetSuccess, OnInternetFailure);

    }


	void OnInternetSuccess()
	{
		GD.Print("Internet good");
	}

	void OnInternetFailure()
	{
		GD.Print("Internet bad");
	}
}