Quick answer: Force the missing generic specialization at compile time. Use AotEnsureMethodGenerated dummy calls, or pre-instantiate generics in a static class.

An IL2CPP build crashes on iOS: ExecutionEngineException: Attempting to call method ‘System.Collections.Generic.List`1[CustomStruct]::ctor’. AOT didn’t generate that specialization.

Why AOT Misses Generics

JIT generates generic specializations at runtime; AOT must enumerate all needed up front. If the IL2CPP build analysis doesn’t see your generic in use, it’s not generated. Reflection-only paths are common offenders.

Force Specialization

[Preserve]
public static class GenericPreservation
{
    private static void EnsureGenerics()
    {
        _ = new List<CustomStruct>();
        _ = new Dictionary<int, CustomStruct>();
        _ = JsonUtility.FromJson<CustomStruct>("");
    }
}

Dummy method referencing each generic instantiation. AOT generates these specializations. Method never executes; static analysis picks it up.

link.xml

<linker>
  <assembly fullname="Assembly-CSharp">
    <type fullname="System.Collections.Generic.List`1[[Game.CustomStruct]]" preserve="all" />
  </assembly>
</linker>

Explicitly preserve the generic instantiation. Linker holds onto it through stripping.

Avoid Runtime Generics

For hot paths, consider non-generic alternatives:

Verifying

Build for iOS. Run; no ExecutionEngineException. Library/il2cpp_cache StrippingReport shows generics included.

“AOT specializes only what it can see. Force the missing ones via dummy references or link.xml.”

JSON deserialization (Newtonsoft) is a common source. Pre-instantiate JsonConvert.DeserializeObject<T> for all DTOs in a startup class.