stride3d/stride · error · InvalidOperationException

Error when generating update engine code for

Error message

Error when generating update engine code for {0}

What it means

UpdateEngineProcessor.ProcessSerializers generates update-engine member code for each serializable type via ProcessType. If ProcessType throws for a type, the exception is wrapped in this InvalidOperationException naming the TypeDefinition, preserving the root cause as InnerException.

Solutions

  1. Read the InnerException to find the exact member/signature that failed
  2. Align the type's update methods with the expected UpdateEngine signatures
  3. Annotate unsupported members appropriately or exclude the type from update-engine processing
  4. Fix any preceding serializer-generation errors for the type first

Example fix

// before
class Enemy : IUpdate
{
    public void Update(int dt, object extra) {} // wrong signature
}
// after
class Enemy : IUpdate
{
    public void Update(int dt) {} // matches expected signature
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate updatable types conform to UpdateEngine conventions before processing:
foreach (var m in typeof(Enemy).GetMethods())
    if (m.Name == "Update" && m.GetParameters().Length > 1)
        Console.WriteLine($"[warn] {m} does not match expected Update signature");

Type guard

static bool MatchesUpdateSignature(MethodInfo m) =>
    m.Name == "Update" && m.GetParameters().All(p => p.ParameterType.IsPrimitive || p.ParameterType.IsValueType);

Try / catch

try { ProcessSerializers(types); }
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Error when generating update engine code"))
{
    logger.Error($"{ex.Message} -- inner: {ex.InnerException?.Message}");
}

Prevention

When it happens

Trigger: ProcessType fails while emitting IL for a type's updatable members — e.g. unsupported member types, methods with unexpected signatures for [Updater]/UpdateEngine conventions, or an earlier serializer error surfacing during engine codegen.

Common situations: A class implementing UpdateEngine interfaces whose member/update method signatures don't match expectations; a type whose serializer generation already failed upstream; stride source-level changes to updatable conventions.

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/a7dafc8a7d7254ef. Report an issue: GitHub.

Appendix: source

Thrown at sources/core/Stride.Core.AssemblyProcessor/UpdateEngineProcessor.cs:194

        {
            // Special case: when processing Stride.Engine assembly, we automatically add dependent assemblies types too
            if (!serializableType.Value.Local && strideEngineAssembly != context.Assembly)
                continue;

            if (serializableType.Key is not TypeDefinition typeDefinition)
                continue;

            // Ignore already processed types
            if (!processedTypes.Add(typeDefinition))
                continue;

            try
            {
                ProcessType(context, module.ImportReference(typeDefinition), mainPrepareMethod);
            }
            catch (Exception e)
            {
                throw new InvalidOperationException(string.Format("Error when generating update engine code for {0}", typeDefinition), e);
            }
        }

        // Force generic instantiations — register resolvers for lists, arrays, and trigger generic update methods
        // Generates calls like:
        //   UpdateEngine.RegisterMemberResolver(new ListUpdateResolver<ElementType>());
        //   UpdateEngine.RegisterMemberResolver(new ArrayUpdateResolver<ElementType>());
        //   ParameterCollectionResolver.InstantiateValueAccessor<KeyType>();  // iOS AOT only
        //   UpdateGeneric_TypeName<T1, T2>();  // for closed generic types
        var il = new ILBuilder(mainPrepareMethod.Body, module);
        foreach (var serializableType in context.SerializableTypesProfiles.SelectMany(x => x.Value.SerializableTypes).ToArray())
        {
            // Special case: when processing Stride.Engine assembly, we automatically add dependent assemblies types too
            if (!serializableType.Value.Local && strideEngineAssembly != context.Assembly)
                continue;

            // Try to find if original method definition was generated
            var typeDefinition = serializableType.Key.Resolve();

View on GitHub (pinned to 96fad776d2)