{"record":{"id":"5dce957370f9622b","repo":"pardeike/Harmony","slug":"unbalanced-exception-markers-cannot-rewrite","errorCode":null,"errorMessage":"Unbalanced exception markers – cannot rewrite.","messagePattern":"Unbalanced exception markers – cannot rewrite\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"Harmony/Internal/FaultBlockRewriter.cs","lineNumber":68,"sourceCode":"\r\n\t\t\tvar i = 0;\r\n\t\t\tvar rewritten = new List<CodeInstruction>(instructions.Count * 2);\r\n\t\t\twhile (i < instructions.Count)\r\n\t\t\t{\r\n\t\t\t\tvar cur = instructions[i];\r\n\r\n\t\t\t\tif (cur.HasBlock(ExceptionBlockType.BeginFaultBlock) == false)\r\n\t\t\t\t{\r\n\t\t\t\t\trewritten.Add(new CodeInstruction(cur));\r\n\t\t\t\t\t++i;\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tvar beginExceptionIdx = FindMatchingBeginException(rewritten);\r\n\t\t\t\tvar endExceptionIdx = FindMatchingEndException(instructions, i + 1);\r\n\r\n\t\t\t\tif (beginExceptionIdx < 0 || endExceptionIdx < 0)\r\n\t\t\t\t\tthrow new InvalidOperationException(\"Unbalanced exception markers – cannot rewrite.\");\r\n\r\n\t\t\t\tvar faultBody = new List<CodeInstruction>();\r\n\t\t\t\tfor (var k = i; k < endExceptionIdx; ++k)\r\n\t\t\t\t\tfaultBody.Add(CloneWithoutFaultMarker(instructions[k]));\r\n\r\n\t\t\t\ti = endExceptionIdx + 1;\r\n\r\n\t\t\t\tvar failedLocal = generator.DeclareLocal(typeof(bool));\r\n\t\t\t\tvar skipFault = generator.DefineLabel();\r\n\r\n\t\t\t\trewritten.AddRange([\r\n\t\t\t\t\tNop.WithBlocks(new ExceptionBlock(ExceptionBlockType.BeginCatchBlock, typeof(object))),\r\n\t\t\t\t\tPop,\r\n\t\t\t\t\tLdc_I4_1,\r\n\t\t\t\t\tStloc[failedLocal.LocalIndex],\r\n\t\t\t\t\tRethrow,\r\n\t\t\t\t\tNop.WithBlocks(new ExceptionBlock(ExceptionBlockType.BeginFinallyBlock)),\r\n\t\t\t\t\tLdloc[failedLocal.LocalIndex],\r","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Internal/FaultBlockRewriter.cs#L50-L86","documentation":"FaultBlockRewriter converts C# 'fault' style exception blocks (identified by special fault markers) into rewritten IL. During Rewrite it pairs each fault marker with a matching begin/end exception marker; if either matching marker cannot be found the instruction stream is unbalanced and rewriting cannot proceed safely, so it throws InvalidOperationException rather than producing corrupt IL.","triggerScenarios":"Rewrite() processing an instruction list whose fault markers do not line up with FindMatchingBeginException/FindMatchingEndException results — e.g. transpilers inserted or removed exception-block boundary instructions, or nested/reordered try-fault blocks breaking marker pairing.","commonSituations":"A custom transpiler that deletes or duplicates instructions inside a method containing fault blocks; combining Harmony patches on methods compiled with unusual exception-region layouts; older/newer compiler output that confuses the marker matcher.","solutions":["Review your transpilers for instructions that remove, reorder, or clone fault/exception block boundaries (Branch, Leave, marker instructions)","Exclude the method from fault rewriting or patch a wrapper method instead","Upgrade Harmony in case the marker-matching logic handles your IL pattern in a newer release","Inspect the method's exception regions with the CLI or MethodBody.ExceptionHandlingClauses to understand the block layout"],"exampleFix":"// before (transpiler deletes fault-block boundary)\ncodes.RemoveAll(c => c.opcode == OpCodes.Leave);\n// after (only remove leaves outside exception regions)\ncodes.RemoveAll(c => c.opcode == OpCodes.Leave && !IsInsideExceptionRegion(codes, c));","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { /* transpile via harmony.Patch with your transpiler */ }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Unbalanced exception markers\")) { /* fall back: patch without that transpiler or skip method */ }","preventionTips":["Never delete/reorder exception-region boundary instructions (Leave, marker code) in transpilers","Test transpilers against methods that contain fault/finally blocks","Clone whole instructions and keep block structure intact when editing CodeInstruction lists","Prefer inserting over removing IL in exception-heavy methods"],"tags":["il-rewriting","transpiler","exception-handling","harmony"],"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"}