{"record":{"id":"6a81b6c9a126c86f","repo":"pardeike/Harmony","slug":"instruction-is-not-a-load-or-store","errorCode":null,"errorMessage":"Instruction is not a load or store","messagePattern":"Instruction is not a load or store","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"Harmony/Tools/Extensions.cs","lineNumber":650,"sourceCode":"\t\t\t{\r\n\t\t\t\tif (code.operand is LocalBuilder localBuilder)\r\n\t\t\t\t\treturn localBuilder.LocalIndex;\r\n\t\t\t\treturn Convert.ToInt32(code.operand);\r\n\t\t\t}\r\n\t\t\telse if (code.opcode == OpCodes.Stloc_S || code.opcode == OpCodes.Stloc)\r\n\t\t\t{\r\n\t\t\t\tif (code.operand is LocalBuilder localBuilder)\r\n\t\t\t\t\treturn localBuilder.LocalIndex;\r\n\t\t\t\treturn Convert.ToInt32(code.operand);\r\n\t\t\t}\r\n\t\t\telse if (code.opcode == OpCodes.Ldloca_S || code.opcode == OpCodes.Ldloca)\r\n\t\t\t{\r\n\t\t\t\tif (code.operand is LocalBuilder localBuilder)\r\n\t\t\t\t\treturn localBuilder.LocalIndex;\r\n\t\t\t\treturn Convert.ToInt32(code.operand);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tthrow new ArgumentException(\"Instruction is not a load or store\", nameof(code));\r\n\t\t}\r\n\r\n\t\t/// <summary>Returns the index targeted by this <c>ldarg</c>, <c>ldarga</c>, or <c>starg</c></summary>\r\n\t\t/// <param name=\"code\">The <see cref=\"CodeInstruction\"/></param>\r\n\t\t/// <returns>The index it targets</returns>\r\n\t\t/// <seealso cref=\"CodeInstruction.LoadArgument(int, bool)\"/>\r\n\t\t/// <seealso cref=\"CodeInstruction.StoreArgument(int)\"/>\r\n\t\tpublic static int ArgumentIndex(this CodeInstruction code)\r\n\t\t{\r\n\t\t\tif (code.opcode == OpCodes.Ldarg_0)\r\n\t\t\t\treturn 0;\r\n\t\t\telse if (code.opcode == OpCodes.Ldarg_1)\r\n\t\t\t\treturn 1;\r\n\t\t\telse if (code.opcode == OpCodes.Ldarg_2)\r\n\t\t\t\treturn 2;\r\n\t\t\telse if (code.opcode == OpCodes.Ldarg_3)\r\n\t\t\t\treturn 3;\r\n\t\t\telse if (code.opcode == OpCodes.Ldarg_S || code.opcode == OpCodes.Ldarg)\r","sourceCodeStart":632,"sourceCodeEnd":668,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Tools/Extensions.cs#L632-L668","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nvar idx = instr.LocalIndex();\n// after\nif (instr.IsLdloc() || instr.IsStloc())\n    var idx = instr.LocalIndex();","handlingStrategy":"validation","validationCode":"static int? TryLocalIndex(CodeInstruction code) =>\n    code.IsLdloc() || code.IsStloc() ? code.LocalIndex() : null;","typeGuard":"static bool IsLocalAccess(CodeInstruction c) =>\n    c.opcode == OpCodes.Ldloc || c.opcode == OpCodes.Ldloc_S ||\n    c.opcode == OpCodes.Ldloca || c.opcode == OpCodes.Ldloca_S ||\n    c.opcode == OpCodes.Stloc || c.opcode == OpCodes.Stloc_S;","tryCatchPattern":"int idx;\ntry { idx = instr.LocalIndex(); }\ncatch (ArgumentException) { continue; // not a local access instruction }","preventionTips":["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"],"tags":["argument-exception","il-transpiler","harmony"],"backgroundTag":"invalid-argument-value","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"}