{"record":{"id":"3baf81791fbcb064","repo":"pardeike/Harmony","slug":"e0","errorCode":null,"errorMessage":"E0","messagePattern":"E0","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"info","filePath":"HarmonyTests/Patching/FinalizerPatches.cs","lineNumber":64,"sourceCode":"\t\t[HarmonyPatch(typeof(Class), nameof(Class.Test))]\r\n\t\t[HarmonyPatchCategory(\"finalizer-test\")]\r\n\t\t[HarmonyPriority(Priority.High)]\r\n\t\tstatic class Patch2\r\n\t\t{\r\n\t\t\tstatic Exception Finalizer(Exception __exception)\r\n\t\t\t{\r\n\t\t\t\t_ = progress.Append($\"Finalizer 2 {__exception?.Message ?? \"-\"} -> E-2\\n\");\r\n\t\t\t\treturn new Exception(\"E-2\");\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tstatic class Class\r\n\t{\r\n\t\tpublic static void Test()\r\n\t\t{\r\n\t\t\tTestTools.WriteLine(\"Test\", false);\r\n\t\t\tthrow new Exception(\"E0\");\r\n\t\t}\r\n\t}\r\n\r\n\t[TestFixture, NonParallelizable]\r\n\tpublic class FinalizerPatches2 : TestLogger\r\n\t{\r\n\t\tstatic Dictionary<string, object> info;\r\n\r\n\t\t[Test]\r\n\t\tpublic void Test_NoThrowingVoidMethod_EmptyFinalizer()\r\n\t\t{\r\n\t\t\tPatch();\r\n\t\t\tAssertNoThrownException();\r\n\t\t\tAssertGotNoResult();\r\n\t\t}\r\n\r\n\t\t[Test]\r\n\t\tpublic void Test_NoThrowingVoidMethod_EmptyFinalizerWithExceptionArg()\r","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/HarmonyTests/Patching/FinalizerPatches.cs#L46-L82","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add a finalizer patch method (static void/bool Finalizer(Exception __exception)) to the patch class so 'E0' is observed and handled instead of escaping.","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.","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.","Verify the patch is applied to the correct target (static Class.Test) — a mistyped HarmonyPatch target leaves the method unpatched.","Pass 'E0' through unchanged if downstream assertions compare ex.Message; do not wrap it in another exception type."],"exampleFix":"// before: target throws, no finalizer\nstatic class Class\n{\n    public static void Test()\n    {\n        TestTools.WriteLine(\"Test\", false);\n        throw new Exception(\"E0\");\n    }\n}\n\n// after: finalizer patch consumes the exception\n[HarmonyPatch(typeof(Class), nameof(Class.Test))]\nstatic class Class_Patch\n{\n    static void Finalizer(Exception __exception)\n    {\n        TestTools.WriteLine($\"caught {__exception?.Message}\", false);\n        // returning normally swallows 'E0'\n    }\n}","handlingStrategy":"try-catch","validationCode":"var m = typeof(Class).GetMethod(nameof(Class.Test), BindingFlags.Public | BindingFlags.Static);\nif (!m.IsStatic) throw new InvalidOperationException(\"Expected static Class.Test for finalizer patching\");","typeGuard":"static bool IsE0(Exception ex) => ex?.Message == \"E0\";","tryCatchPattern":"[HarmonyPatch(typeof(Class), nameof(Class.Test))]\nstatic class ClassPatch\n{\n    static bool Finalizer(Exception __exception)\n    {\n        if (__exception != null && __exception.Message == \"E0\")\n            return true; // swallow the controlled test exception\n        return false;    // rethrow anything unexpected\n    }\n}","preventionTips":["Declare a finalizer with an Exception __exception parameter whenever patching methods that can throw.","Use the finalizer's bool return (true = swallow) to control propagation deterministically.","Assert on exception message ('E0') inside the finalizer so unrelated exceptions still propagate.","Confirm patch application with Harmony.GetPatchInfo before invoking the target.","Keep TestLogger/NonParallelizable fixtures isolated so 'E0' is never attributed to another test."],"tags":["harmony","exception-handling","finalizer-patch","test-fixture"],"backgroundTag":"internal-invariant-violation","analyzedSha":"e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c","analyzedAt":"2026-09-15T22:47:11.550Z","contentChangedAt":"2026-09-15T22:47:11.550Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}