pardeike/Harmony · info · Exception

E0

Error message

E0

What it means

This Exception with message 'E0' is thrown intentionally by the static Class.Test() helper in Harmony's FinalizerPatches.cs:64 test fixture, after printing 'Test'. It is the controlled exception payload used to verify that Harmony finalizer patches (methods with an Exception __exception parameter) observe and can consume or rethrow exceptions raised inside patched methods. It is not a library defect; it is the expected exception a finalizer test must catch.

Solutions

  1. Add a finalizer patch method (static void/bool Finalizer(Exception __exception)) to the patch class so 'E0' is observed and handled instead of escaping.
  2. In the finalizer, return true / return void normally to swallow the exception, or set __exception = null depending on the desired semantics, matching what the test asserts.
  3. If you need the exception to propagate for the test, ensure the test harness expects it (e.g. Assert.Throws) rather than letting the runner report it as failure.
  4. Verify the patch is applied to the correct target (static Class.Test) — a mistyped HarmonyPatch target leaves the method unpatched.
  5. Pass 'E0' through unchanged if downstream assertions compare ex.Message; do not wrap it in another exception type.

Example fix

// before: target throws, no finalizer
static class Class
{
    public static void Test()
    {
        TestTools.WriteLine("Test", false);
        throw new Exception("E0");
    }
}

// after: finalizer patch consumes the exception
[HarmonyPatch(typeof(Class), nameof(Class.Test))]
static class Class_Patch
{
    static void Finalizer(Exception __exception)
    {
        TestTools.WriteLine($"caught {__exception?.Message}", false);
        // returning normally swallows 'E0'
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

var m = typeof(Class).GetMethod(nameof(Class.Test), BindingFlags.Public | BindingFlags.Static);
if (!m.IsStatic) throw new InvalidOperationException("Expected static Class.Test for finalizer patching");

Type guard

static bool IsE0(Exception ex) => ex?.Message == "E0";

Try / catch

[HarmonyPatch(typeof(Class), nameof(Class.Test))]
static class ClassPatch
{
    static bool Finalizer(Exception __exception)
    {
        if (__exception != null && __exception.Message == "E0")
            return true; // swallow the controlled test exception
        return false;    // rethrow anything unexpected
    }
}

Prevention

When it happens

Trigger: Invoking the static Class.Test() (via reflection or a patched delegate) during FinalizerPatches2 test runs, e.g. Harmony.PatchAll on the FinalizerPatches assembly followed by TestTools.Run<Class.Test>() style invocation, or any test asserting the finalizer received an exception whose Message == 'E0'. It triggers whenever the patched target executes its unconditional throw.

Common situations: Patch authors replicating Harmony's finalizer tests hit this when their finalizer does not declare the __exception parameter or returns the wrong result sentinel, letting 'E0' escape to the test runner. It also appears when users patch a method that always throws and forget a finalizer, and when porting test code across Harmony versions where finalizer result semantics (bool result vs void) changed.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15). Data as JSON: /api/errors/3baf81791fbcb064. Report an issue: GitHub.

Appendix: source

Thrown at HarmonyTests/Patching/FinalizerPatches.cs:64

		[HarmonyPatch(typeof(Class), nameof(Class.Test))]
		[HarmonyPatchCategory("finalizer-test")]
		[HarmonyPriority(Priority.High)]
		static class Patch2
		{
			static Exception Finalizer(Exception __exception)
			{
				_ = progress.Append($"Finalizer 2 {__exception?.Message ?? "-"} -> E-2\n");
				return new Exception("E-2");
			}
		}
	}

	static class Class
	{
		public static void Test()
		{
			TestTools.WriteLine("Test", false);
			throw new Exception("E0");
		}
	}

	[TestFixture, NonParallelizable]
	public class FinalizerPatches2 : TestLogger
	{
		static Dictionary<string, object> info;

		[Test]
		public void Test_NoThrowingVoidMethod_EmptyFinalizer()
		{
			Patch();
			AssertNoThrownException();
			AssertGotNoResult();
		}

		[Test]
		public void Test_NoThrowingVoidMethod_EmptyFinalizerWithExceptionArg()

View on GitHub (pinned to e7872dc170)