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
- Assign the Game property before FinishedLaunching executes — typically in the AppDelegate constructor or as a field initializer: Game = new MyGame();
- If creation is deferred, override FinishedLaunching and assign Game as the first statement before calling base.
- 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
- Initialize Game in the AppDelegate constructor or a field initializer so it exists before any UIKit callback.
- Keep the Stride iOS template's ordering when customizing the delegate.
- Never defer Game creation until after lifecycle callbacks.
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
- AVFoundationVideoBackend already initialized.
- Cannot register new platforms. RegisterSupportedPlatforms…
- The path [ ] contains access to a member of a null object.
- Trying to dispose a lock that has already been released.
- The default profile cannot be unloaded
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)