stride3d/stride · error · InvalidOperationException

Cannot create a swapchain: Vulkan surface extensions are…

Error message

Cannot create a swapchain: Vulkan surface extensions are not available. This may happen when using a headless ICD.

What it means

Before creating a VkSurfaceKHR, CreateSurface checks that the Vulkan instance was created with the required surface extensions (VK_KHR_surface plus platform ones) via GraphicsAdapterFactory.GetInstance(...).HasSurfaceSupport. If the instance lacks surface support — typical of headless ICDs or compute-only environments — it throws InvalidOperationException because no swapchain can be presented.

Solutions

  1. Install full graphics drivers and a display server (X11/Wayland) so the Vulkan loader exposes surface extensions.
  2. On WSL2, enable WSLg or run the app natively on Windows with D3D backend.
  3. Use the D3D11/D3D12 backend on Windows instead of Vulkan when only compute/headless Vulkan is available.
  4. For CI/offscreen rendering, avoid swapchain creation and render to offscreen textures instead.

Example fix

// before (container without display)
dotnet StrideApp.dll
// after: enable GPU + surface support
docker run --gpus all -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix ...
Defensive patterns

Strategy: validation

Validate before calling

if (!GraphicsAdapterFactory.GetInstance(debug).HasSurfaceSupport)
    throw new InvalidOperationException("Vulkan instance lacks surface extensions; cannot present");

Type guard

bool CanPresent(GraphicsAdapterFactory f) => f.GetInstance(isDebug: false).HasSurfaceSupport;

Try / catch

try { presenter = new SwapChainGraphicsPresenter(device, desc); }
catch (InvalidOperationException ex) when (ex.Message.Contains("headless ICD")) { log.Warn("No Vulkan surface support; falling back to D3D11 or offscreen rendering"); SwitchToAlternativeBackend(); }

Prevention

When it happens

Trigger: SwapChainGraphicsPresenter creation/recreation on a Vulkan instance without VK_KHR_*_surface extensions enabled, i.e. when HasSurfaceSupport is false (headless ICD, no display server, WSL without GPU surface support).

Common situations: Running in CI/containers with software Vulkan (lavapipe/headless ICD); SSH sessions without DISPLAY/WAYLAND_DISPLAY; WSL2 without WSLg; installing only compute drivers (e.g. NVIDIA compute-only driver) on servers.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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

Appendix: source

Thrown at sources/engine/Stride.Graphics/Vulkan/SwapChainGraphicsPresenter.Vulkan.cs:557

            if (surface != VkSurfaceKHR.Null)
            {
                GraphicsDevice.NativeInstanceApi.vkDestroySurfaceKHR(GraphicsDevice.NativeInstance, surface, null);
                surface = VkSurfaceKHR.Null;
            }
            CreateSurface();
        }

        private unsafe void CreateSurface()
        {
            // Check for Window Handle parameter
            if (Description.DeviceWindowHandle == null)
            {
                throw new ArgumentException("DeviceWindowHandle cannot be null");
            }

            // Validate surface extension support (not available with headless ICDs)
            if (!GraphicsAdapterFactory.GetInstance(GraphicsDevice.IsDebugMode).HasSurfaceSupport)
                throw new InvalidOperationException("Cannot create a swapchain: Vulkan surface extensions are not available. This may happen when using a headless ICD.");

            // Create surface
#if STRIDE_UI_SDL
            // iOS reuses the SDL surface-creation path (GameContextiOS inherits GameContextSDL);
            // SDL's VulkanCreateSurface routes to VkMetalSurfaceCreateInfoEXT internally on iOS.
            if (Description.DeviceWindowHandle.Context == Games.AppContextType.DesktopSDL
                || Description.DeviceWindowHandle.Context == Games.AppContextType.iOS)
            {
                var control = Description.DeviceWindowHandle.NativeWindow as SDL.Window;
                Silk.NET.Core.Native.VkNonDispatchableHandle surfaceHandle = default;
                SDL.Window.SDL.VulkanCreateSurface((Silk.NET.SDL.Window*)control.SdlHandle, new Silk.NET.Core.Native.VkHandle(GraphicsDevice.NativeInstance.Handle), ref surfaceHandle);
                surface = new VkSurfaceKHR(surfaceHandle.Handle);
            }
            else
#endif
            if (Platform.Type == PlatformType.Windows)
            {
                var controlHandle = Description.DeviceWindowHandle.Handle;

View on GitHub (pinned to 96fad776d2)