stride3d/stride · error · InvalidOperationException

Game must be assigned before base.FinishedLaunching.

Error message

Game must be assigned before base.FinishedLaunching.

What it means

StrideApplicationDelegate.FinishedLaunching (iOS/UIKit lifecycle callback) requires the static Game property to be assigned before base lifecycle processing continues, throwing otherwise. The delegate needs a live Game to create the SDL window and start the game loop on the main thread; launching without one cannot proceed.

Solutions

  1. Assign the Game property before FinishedLaunching executes — typically in the AppDelegate constructor or as a field initializer: Game = new MyGame();
  2. If creation is deferred, override FinishedLaunching and assign Game as the first statement before calling base.
  3. Verify the iOS bootstrap order so no UIKit callback reaches the delegate before Game is set.

Example fix

// before
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    return base.FinishedLaunching(app, options); // Game is null
}
// after
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    Game = Game ?? new MyGame();
    return base.FinishedLaunching(app, options);
}
Defensive patterns

Strategy: validation

Validate before calling

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    if (Game is null) Game = new MyGame();
    return base.FinishedLaunching(app, options);
}

Type guard

bool ReadyToLaunch() => Game != null;

Try / catch

try { return base.FinishedLaunching(app, options); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Game must be assigned"))
{
    Game = new MyGame();
    return base.FinishedLaunching(app, options);
}

Prevention

When it happens

Trigger: AppDelegate overrides FinishedLaunching and calls base.FinishedLaunching(application, launchOptions) (or lets the base run) without setting Game = new MyGame() beforehand.

Common situations: Forgetting the Game assignment in a custom AppDelegate, reordering startup code so FinishedLaunching runs before the field initializer, or template refactors that moved Game creation after the lifecycle callback.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/8f2ec950db5d4c63. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Games/Starter/StrideApplicationDelegate.cs:27

namespace Stride.Starter
{
    /// <summary>
    /// UIApplicationDelegate base for Stride iOS games. Subclass and assign <see cref="Game"/>
    /// in <c>FinishedLaunching</c> before calling <c>base.FinishedLaunching</c>; the base creates
    /// an SDL window + iOS GameContext and schedules <see cref="GameBase.Run"/> on the next
    /// main-loop tick so launch finishes before the (blocking) game loop takes over.
    /// </summary>
    public class StrideApplicationDelegate : UIApplicationDelegate
    {
        protected GameBase Game { get; set; }

        private Window sdlWindow;

        public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
        {
            if (Game == null)
                throw new InvalidOperationException("Game must be assigned before base.FinishedLaunching.");

            sdlWindow = new Window("Stride");

            // Game.Run blocks; iOS needs FinishedLaunching to return for launch to complete.
            BeginInvokeOnMainThread(() =>
            {
                try { Game.Run(new GameContextiOS(sdlWindow)); }
                finally { sdlWindow?.Dispose(); sdlWindow = null; }
            });

            return true;
        }

        public override void WillTerminate(UIApplication application)
        {
            Game?.Exit();
            Game?.Dispose();
            Game = null;

View on GitHub (pinned to 96fad776d2)