pardeike/Harmony · error · ArgumentException
Instruction is not a load or store
Error message
Instruction is not a load or store
What it means
Harmony's LocalIndex() extension reads the local-variable slot targeted by an IL instruction, unwrapping a LocalBuilder operand or converting an integer operand. It throws ArgumentException when the instruction's opcode is neither a local load (ldloc/ldloca variants) nor a local store (stloc variants), because only those opcodes carry a local index operand. This is an assertion that the caller passed a local-access instruction.
Solutions
- Before calling LocalIndex(), check the opcode with code.IsLdloc() / code.IsStloc() (or a switch over OpCodes.Ldloc*, Ldloca*, Stloc*) and skip non-local instructions
- Restrict the matching pattern in your transpiler (CodeMatch with the ldloc/stloc opcodes) so LocalIndex() is only reached for real local accesses
- If the intent was an argument index, use ArgumentIndex() instead; if a general operand index is needed, guard with code.operand is LocalBuilder or int
Example fix
// before
var idx = instr.LocalIndex();
// after
if (instr.IsLdloc() || instr.IsStloc())
var idx = instr.LocalIndex(); Defensive patterns
Strategy: validation
Validate before calling
static int? TryLocalIndex(CodeInstruction code) =>
code.IsLdloc() || code.IsStloc() ? code.LocalIndex() : null; Type guard
static bool IsLocalAccess(CodeInstruction c) =>
c.opcode == OpCodes.Ldloc || c.opcode == OpCodes.Ldloc_S ||
c.opcode == OpCodes.Ldloca || c.opcode == OpCodes.Ldloca_S ||
c.opcode == OpCodes.Stloc || c.opcode == OpCodes.Stloc_S; Try / catch
int idx;
try { idx = instr.LocalIndex(); }
catch (ArgumentException) { continue; // not a local access instruction } Prevention
- Always opcode-check before asking for a local or argument index
- Use CodeMatch patterns so only intended instructions reach index extraction
- Remember Debug and Release builds emit different local/opcode patterns; don't assume one shape
When it happens
Trigger: Calling CodeInstruction.LocalIndex() on an instruction whose opcode is not Ldloc/Ldloc_S/Ldloca/Ldloca_S/Stloc/Stloc_S, e.g. a call, br, or ldarg instruction, typically inside an IL transpiler loop that assumed all instructions in a range are local accesses.
Common situations: Transpiler authors scanning a method body and blindly calling LocalIndex() on every instruction; cursor/emitter code filtering locals after a refactor changed which instructions are matched; assumptions broken by compiler output differences (Debug vs Release IL) that changed opcode sequences.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- The type must declare an empty constructor (the constructor…
- position( ) + count( ) > buffer.Length( )
- Unbalanced exception markers – cannot rewrite.
- Multiple loaded HarmonySharedState types prevent safe…
- Unsupported inline signature parameter type
AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15).
Data as JSON: /api/errors/6a81b6c9a126c86f.
Report an issue: GitHub.
Appendix: source
Thrown at Harmony/Tools/Extensions.cs:650
{
if (code.operand is LocalBuilder localBuilder)
return localBuilder.LocalIndex;
return Convert.ToInt32(code.operand);
}
else if (code.opcode == OpCodes.Stloc_S || code.opcode == OpCodes.Stloc)
{
if (code.operand is LocalBuilder localBuilder)
return localBuilder.LocalIndex;
return Convert.ToInt32(code.operand);
}
else if (code.opcode == OpCodes.Ldloca_S || code.opcode == OpCodes.Ldloca)
{
if (code.operand is LocalBuilder localBuilder)
return localBuilder.LocalIndex;
return Convert.ToInt32(code.operand);
}
else
throw new ArgumentException("Instruction is not a load or store", nameof(code));
}
/// <summary>Returns the index targeted by this <c>ldarg</c>, <c>ldarga</c>, or <c>starg</c></summary>
/// <param name="code">The <see cref="CodeInstruction"/></param>
/// <returns>The index it targets</returns>
/// <seealso cref="CodeInstruction.LoadArgument(int, bool)"/>
/// <seealso cref="CodeInstruction.StoreArgument(int)"/>
public static int ArgumentIndex(this CodeInstruction code)
{
if (code.opcode == OpCodes.Ldarg_0)
return 0;
else if (code.opcode == OpCodes.Ldarg_1)
return 1;
else if (code.opcode == OpCodes.Ldarg_2)
return 2;
else if (code.opcode == OpCodes.Ldarg_3)
return 3;
else if (code.opcode == OpCodes.Ldarg_S || code.opcode == OpCodes.Ldarg)
View on GitHub (pinned to e7872dc170)