pardeike/Harmony · error · ArgumentOutOfRangeException

Instruction offset is less than 0

Error message

Instruction offset {offset} is less than 0

What it means

While re-emitting/copied IL, MethodBodyReader.GetInstruction validates that a requested instruction offset is non-negative. A negative offset means the operand resolution computed a branch/jump target below the start of the method body, which indicates a malformed or mis-decoded IL stream rather than a problem in the user's patch code. Harmony throws ArgumentOutOfRangeException with the offset value for diagnosis.

Solutions

  1. Check whether the target method is obfuscated and deobfuscate or patch a different method
  2. Narrow which transpiler triggers the failure by removing transpilers one at a time
  3. Report the failing method (FullDescription) to Harmony maintainers if ordinary IL triggers it — likely a reader bug
  4. Avoid writing transpilers that construct CodeInstruction offsets/labels manually; rely on Harmony's label APIs

Example fix

// before
foreach (var ci in instructions) // blindly rewriting, can corrupt offsets
    if (ci.opcode == OpCodes.Br) ci.operand = someAbsoluteIntOffset;
// after
foreach (var ci in instructions)
    if (ci.opcode == OpCodes.Br)
    {
        var lbl = generator.DefineLabel();
        ci.labels.Add(lbl);
        ci.opcode = OpCodes.Br;
        ci.operand = lbl; // use labels, not raw offsets
    }
Defensive patterns

Strategy: try-catch

Validate before calling

// no pre-call validation is practical; validate at patch-install time
try { harmony.Patch(target, transpiler: myTranspiler); Log.Info("transpiler ok"); }
catch (ArgumentOutOfRangeException ex) { Log.Error($"{target} IL decode failed: {ex.Message}"); }

Try / catch

try { harmony.Patch(target, transpiler: trans); }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "offset")
{ Log.Error($"IL offset issue while transpiling {target.FullDescription()}: {ex.Message}"); throw; }

Prevention

When it happens

Trigger: Transpiling a method whose IL contains switch/branch targets that decode to negative offsets; processing an IL stream read from a method body that the reader mis-parses (obfuscated or exotic IL); bugs in custom transpilers that manufacture instructions with negative offsets.

Common situations: Transpiling obfuscated game assemblies where IL is manipulated to confuse decompilers; methods with unusual switch tables on older Mono runtimes; transpilers replacing/inserting instructions with computed labels incorrectly.

Related errors


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

Appendix: source

Thrown at Harmony/Internal/MethodCopier.cs:609

						}
					}
					else
					{
						instruction.operand = GetParameter(idx);
						instruction.argument = idx;
					}
					break;
				}

				default:
					throw new NotSupportedException();
			}
		}

		ILInstruction GetInstruction(int offset, bool isEndOfInstruction)
		{
			if (offset < 0)
				throw new ArgumentOutOfRangeException(nameof(offset), offset, $"Instruction offset {offset} is less than 0");

			var lastInstructionIndex = ilInstructions.Count - 1;
			var instruction = ilInstructions[lastInstructionIndex];
			if (offset > instruction.offset + instruction.GetSize() - 1)
				throw new ArgumentOutOfRangeException(nameof(offset), offset, $"Instruction offset {offset} is outside valid range 0 - {instruction.offset + instruction.GetSize() - 1}");

			var min = 0;
			var max = lastInstructionIndex;
			while (min <= max)
			{
				var mid = min + ((max - min) / 2);
				instruction = ilInstructions[mid];

				if (isEndOfInstruction)
				{
					if (offset == instruction.offset + instruction.GetSize() - 1)
						return instruction;
				}

View on GitHub (pinned to e7872dc170)