{"record":{"id":"e7334a9eb38eaec7","repo":"stride3d/stride","slug":"devicewindowhandle-cannot-be-null-swapchaingraphicspresenter","errorCode":null,"errorMessage":"DeviceWindowHandle cannot be null","messagePattern":"DeviceWindowHandle cannot be null","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.Graphics/Vulkan/SwapChainGraphicsPresenter.Vulkan.cs","lineNumber":552,"sourceCode":"\n        private unsafe void RecreateSurface()\n        {\n            // Swapchain must be torn down before the surface (Vulkan spec + Android ANativeWindow exclusivity).\n            DestroySwapchain();\n            if (surface != VkSurfaceKHR.Null)\n            {\n                GraphicsDevice.NativeInstanceApi.vkDestroySurfaceKHR(GraphicsDevice.NativeInstance, surface, null);\n                surface = VkSurfaceKHR.Null;\n            }\n            CreateSurface();\n        }\n\n        private unsafe void CreateSurface()\n        {\n            // Check for Window Handle parameter\n            if (Description.DeviceWindowHandle == null)\n            {\n                throw new ArgumentException(\"DeviceWindowHandle cannot be null\");\n            }\n\n            // Validate surface extension support (not available with headless ICDs)\n            if (!GraphicsAdapterFactory.GetInstance(GraphicsDevice.IsDebugMode).HasSurfaceSupport)\n                throw new InvalidOperationException(\"Cannot create a swapchain: Vulkan surface extensions are not available. This may happen when using a headless ICD.\");\n\n            // Create surface\n#if STRIDE_UI_SDL\n            // iOS reuses the SDL surface-creation path (GameContextiOS inherits GameContextSDL);\n            // SDL's VulkanCreateSurface routes to VkMetalSurfaceCreateInfoEXT internally on iOS.\n            if (Description.DeviceWindowHandle.Context == Games.AppContextType.DesktopSDL\n                || Description.DeviceWindowHandle.Context == Games.AppContextType.iOS)\n            {\n                var control = Description.DeviceWindowHandle.NativeWindow as SDL.Window;\n                Silk.NET.Core.Native.VkNonDispatchableHandle surfaceHandle = default;\n                SDL.Window.SDL.VulkanCreateSurface((Silk.NET.SDL.Window*)control.SdlHandle, new Silk.NET.Core.Native.VkHandle(GraphicsDevice.NativeInstance.Handle), ref surfaceHandle);\n                surface = new VkSurfaceKHR(surfaceHandle.Handle);\n            }","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.Graphics/Vulkan/SwapChainGraphicsPresenter.Vulkan.cs#L534-L570","documentation":"CreateSurface needs a target window handle to create the platform VkSurfaceKHR; if SwapChainGraphicsPresenter's Description.DeviceWindowHandle is null it throws ArgumentException. Without a window there is nothing to present to, and the non-headless code path cannot proceed.","triggerScenarios":"Constructing or recreating a SwapChainGraphicsPresenter with a SwapChainDescription whose DeviceWindowHandle was never set (null).","commonSituations":"Building a Game/graphics context programmatically and forgetting to assign Game.Window / DeviceWindowHandle; creating the presenter before the game window exists; copying a description struct that dropped the handle.","solutions":["Set Description.DeviceWindowHandle to a valid window (e.g. game.Window.NativeWindow or new WindowHandle(context, windowPtr)) before creating the presenter.","Ensure the game window is created before graphics device/presenter initialization.","For offscreen rendering, use the headless path instead of a swapchain presenter."],"exampleFix":"// before\nvar presenter = new SwapChainGraphicsPresenter(device, new SwapChainDescription { ... });\n// after\nvar desc = new SwapChainDescription { ... };\ndesc.DeviceWindowHandle = game.Window.Handle;\nvar presenter = new SwapChainGraphicsPresenter(device, desc);","handlingStrategy":"validation","validationCode":"if (swapChainDescription.DeviceWindowHandle == null)\n    throw new InvalidOperationException(\"Set DeviceWindowHandle before creating the SwapChainGraphicsPresenter\");","typeGuard":"bool HasWindowHandle(SwapChainDescription d) => d.DeviceWindowHandle is not null;","tryCatchPattern":"try { presenter = new SwapChainGraphicsPresenter(device, desc); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"DeviceWindowHandle cannot be null\")) { log.Error(\"No window handle configured for swapchain\", ex); throw; }","preventionTips":["Initialize the game window before creating graphics device/presenter","Always set Game.Window handle into SwapChainDescription.DeviceWindowHandle","Use headless/offscreen APIs for rendering without a window","Add an early config check that the window handle is assigned"],"tags":["vulkan","swapchain","window-handle","null-argument"],"backgroundTag":"missing-required-argument","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}