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

  1. Restructure the pinned code so the value is stored to and loaded from a local variable before Interop.Pin
  2. Extract complex expressions into a local first (e.g. var ptr = obj.Field; fixed(...)) so the IL becomes a simple ldloc
  3. Avoid calling Interop.Pin directly on computed/call results; pin simple locals
  4. 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

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


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)