stride3d/stride · error · InvalidOperationException

DeviceWindowHandle cannot be null

Error message

DeviceWindowHandle cannot be null

What it means

SwapChainGraphicsPresenter.CreateSwapChain throws InvalidOperationException when PresentationParameters.DeviceWindowHandle is null, since a D3D swap chain must be created against a valid output window. The XML docs on the method explicitly state the handle must be non-null and its Handle non-zero. This fires in the constructor and in OnRecreated (swap-chain re-creation).

Solutions

  1. Set PresentationParameters.DeviceWindowHandle to a valid window (e.g. gameWindow.NativeWindow? or WindowHandle from your platform window) before creating the presenter/device
  2. For offscreen rendering, use the appropriate offscreen/graphics-context presenter instead of a swap chain presenter
  3. Guard window lifetime: don't trigger OnRecreated/device reinit after the window is disposed
  4. Verify the handle's Handle value is non-zero before init

Example fix

// before
var parameters = new PresentationParameters(); // DeviceWindowHandle is null
var device = GraphicsDevice.New(null, parameters);
// after
var parameters = new PresentationParameters
{
    DeviceWindowHandle = Game.Window.NativeWindow // must be a valid, live window handle
};
var device = GraphicsDevice.New(null, parameters);
Defensive patterns

Strategy: validation

Validate before calling

if (parameters.DeviceWindowHandle is null || parameters.DeviceWindowHandle.Handle == IntPtr.Zero)
    throw new InvalidOperationException("Assign a valid DeviceWindowHandle before creating a D3D presenter.");

Type guard

bool HasValidWindowHandle(PresentationParameters p) =>
    p?.DeviceWindowHandle != null && p.DeviceWindowHandle.Handle != IntPtr.Zero;

Try / catch

try { new SwapChainGraphicsPresenter(device, parameters); }
catch (InvalidOperationException ex) when (ex.Message.Contains("DeviceWindowHandle"))
{
    // fall back to offscreen presenter or re-init with a real window handle
}

Prevention

When it happens

Trigger: Constructing SwapChainGraphicsPresenter (directly or via GraphicsDevice/presenter factory on Direct3D) with PresentationParameters whose DeviceWindowHandle was never set, or calling OnRecreated after the window was closed/disposed leaving the handle null.

Common situations: Running a Stride headless/offscreen setup on a D3D backend without assigning a window handle or using the proper offscreen presenter; forgetting to set GameWindow/window handle before graphics device init; window closed then device recreate triggered; copy-pasting presenter setup from non-windowed contexts.

Related errors


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

Appendix: source

Thrown at sources/engine/Stride.Graphics/Direct3D/SwapChainGraphicsPresenter.Direct3D.cs:556

            DepthStencilBuffer.InitializeFrom(newTextureDescription);

            foreach (var childTexture in childrenTextures)
            {
                childTexture.InitializeFrom(parentTexture: DepthStencilBuffer, in childTexture.ViewDescription);
            }
        }

        /// <summary>
        ///   Creates or reinitializes the Swap-Chain with the current configuration.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        ///   <see cref="PresentationParameters.DeviceWindowHandle"/> is <see langword="null"/> or
        ///   the <see cref="WindowHandle.Handle"/> is invalid or zero.
        /// </exception>
        private void CreateSwapChain()
        {
            if (Description.DeviceWindowHandle is null)
                throw new InvalidOperationException("DeviceWindowHandle cannot be null");

            // D3D presenters don't honor SurfaceRotation (DXGI handles rotation transparently).
            if (SurfaceRotation != SurfaceRotation.Identity)
                throw new NotImplementedException($"SurfaceRotation '{SurfaceRotation}' is not supported on Direct3D presenters.");

#if STRIDE_PLATFORM_UWP
            CreateSwapChainForUWP();
#else
            CreateSwapChainForWindows();
#endif
        }

#if STRIDE_PLATFORM_UWP
        /// <summary>
        ///   Creates or reinitializes the Swap-Chain on the Universal Windows Platform (UWP).
        /// </summary>
        private void CreateSwapChainForUWP()
        {

View on GitHub (pinned to 96fad776d2)