stride3d/stride · error · InvalidOperationException

The . must not be zero.

Error message

The {nameof(WindowHandle)}.{nameof(WindowHandle.Handle)} must not be zero.

What it means

CreateSwapChainForWindows creates a desktop (HWND-based) DXGI swapchain, which requires a real native window handle. If WindowHandle.Handle is IntPtr.Zero there is no window to attach the swapchain to, so Stride throws InvalidOperationException before calling CreateSwapChainForDesktop.

Solutions

  1. Provide a valid non-zero HWND in DeviceWindowHandle.Handle before creating the presenter.
  2. If you only need offscreen rendering, render to a RenderTarget/Texture instead of creating a swapchain.
  3. Ensure presenter creation happens after the native window exists and its handle is cached.

Example fix

// before
var description = new GraphicsPresenterDescription { DeviceWindowHandle = new WindowHandle() };
// after
var description = new GraphicsPresenterDescription { DeviceWindowHandle = new WindowHandle { Handle = form.Handle } };
Defensive patterns

Strategy: validation

Validate before calling

if (description.DeviceWindowHandle == null || description.DeviceWindowHandle.Handle == IntPtr.Zero)
    throw new InvalidOperationException("A valid window handle is required to create a swapchain");

Type guard

bool HasValidHandle(WindowHandle h) => h is { Handle: not 0 };

Try / catch

try { presenter = new SwapChainGraphicsPresenter(device, desc); }
catch (InvalidOperationException ex) when (ex.Message.Contains("must not be zero"))
{ throw new ApplicationException("Initialize the window before creating the presenter", ex); }

Prevention

When it happens

Trigger: Constructing a SwapChainGraphicsPresenter on a non-UWP Direct3D platform while Description.DeviceWindowHandle.Handle == 0, or DeviceWindowHandle being effectively empty despite being non-null.

Common situations: Headless/offscreen rendering attempts on Windows (developers assume a swapchain works without a window); forgetting to pass the actual window handle from the UI framework (WinForms Control.Handle, SDL/WPF HWND) into the presenter description; ordering issues where presenter creation runs before the window is created.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

                    throw new NotSupportedException($"Window context [{Description.DeviceWindowHandle.Context}] not supported while creating SwapChain");
            }

            this.swapChain = swapChain;
            swapChainVersion = GetLatestDxgiSwapChainVersion(swapChain);
        }
#else
        /// <summary>
        ///   Creates or reinitializes the Swap-Chain on the desktop Windows platform.
        /// </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 CreateSwapChainForWindows()
        {
            var hwndPtr = Description.DeviceWindowHandle.Handle;
            if (hwndPtr == 0)
                throw new InvalidOperationException($"The {nameof(WindowHandle)}.{nameof(WindowHandle.Handle)} must not be zero.");

            CreateSwapChainForDesktop(hwndPtr);
        }

        private void CreateSwapChainForDesktop(IntPtr handle)
        {
#if STRIDE_GRAPHICS_API_DIRECT3D12
            useFlipModel = true;
#else
            // https://devblogs.microsoft.com/directx/dxgi-flip-model/#what-do-i-have-to-do-to-use-flip-model
            useFlipModel = Description.MultisampleCount == MultisampleCount.None && flipModelSupport;
#endif

            var swapchainFormat = Description.BackBufferFormat;
            bufferCount = 1;

            if (useFlipModel)
            {

View on GitHub (pinned to 96fad776d2)