stride3d/stride · error · InvalidOperationException
Could not find a load operation right before Interop.Pin
Error message
Could not find a load operation right before Interop.Pin
What it means
InteropProcessor's ReplacePinStatement rewrites Interop.Pin patterns expecting the instruction immediately before it to load a value into a local (ldloc/ldloca or constant/ldarga form). When the previous instruction is not any recognized load, it throws this InvalidOperationException because the pin pattern is malformed.
Solutions
- Restructure the pinned code so the value is stored to and loaded from a local variable before Interop.Pin
- Extract complex expressions into a local first (e.g. var ptr = obj.Field; fixed(...)) so the IL becomes a simple ldloc
- Avoid calling Interop.Pin directly on computed/call results; pin simple locals
- Update Stride version if a newer compiler's IL pattern is unsupported
Example fix
// before Interop.Pin(obj.Inner.Value, out handle); // previous IL is a call/ldfld, not ldloc // after var v = obj.Inner.Value; Interop.Pin(v, out handle); // previous IL is ldloc.0
Defensive patterns
Strategy: validation
Validate before calling
// ensure pinned value is a local: if you cannot write `var v = expr; Interop.Pin(v, ...)` the IL will not match
Try / catch
// build-time: catch in AssemblyProcessor task logs
catch (InvalidOperationException ex) when (ex.Message.Contains("load operation right before Interop.Pin"))
{
// restructure the pinning code to use a local variable
} Prevention
- Pin simple local variables only, not call/field results
- Follow Stride's documented Interop.Pin usage pattern
- Test AssemblyProcessor output after compiler upgrades
When it happens
Trigger: PatchMethod encounters an Interop.Pin call whose preceding IL is not a load operation (e.g. the pinned value comes from a call, field load, or reordered stack operations).
Common situations: Writing custom pinning code whose IL shape differs from the patterns Interop.Pin expects; compiler updates (new Roslyn emit patterns) changing instruction order; chaining expressions so the pinned value isn't a simple local load.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Could not find a store operation right after Interop.Pin
- Could not resolve generic arguments
- Missing mscorlib.dll from assembly
- Unsupported generic resolution.
- FixedBuffer attribute is not supported.
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/be8fb28afd11c285.
Report an issue: GitHub.
Appendix: source
Thrown at sources/core/Stride.Core.AssemblyProcessor/InteropProcessor.cs:180
else if (previousInstruction.OpCode == OpCodes.Ldloc_2)
{
variableIndex = 2;
}
else if (previousInstruction.OpCode == OpCodes.Ldloc_3)
{
variableIndex = 3;
}
else if (previousInstruction.OpCode == OpCodes.Ldloc_S)
{
variableIndex = ((VariableReference)previousInstruction.Operand).Index;
}
else if (previousInstruction.OpCode == OpCodes.Ldloc)
{
variableIndex = ((VariableReference)previousInstruction.Operand).Index;
}
else
{
throw new InvalidOperationException("Could not find a load operation right before Interop.Pin");
}
var variable = ilProcessor.Body.Variables[variableIndex];
variable.VariableType = variable.VariableType.MakePinnedType();
ilProcessor.Remove(previousInstruction);
ilProcessor.Remove(fixedtoPatch);
}
private void ReplaceFixedStatement(MethodDefinition method, ILProcessor ilProcessor, Instruction fixedtoPatch)
{
var paramT = ((GenericInstanceMethod)fixedtoPatch.Operand).GenericArguments[0];
// Preparing locals
// local(0) T& pinned
method.Body.Variables.Add(new VariableDefinition(new PinnedType(new ByReferenceType(paramT))));
int index = method.Body.Variables.Count - 1;
View on GitHub (pinned to 96fad776d2)