neovide/neovide · error
Could not create composition target
Error message
Could not create composition target
What it means
Thrown when IDCompositionDevice::CreateTargetForHwnd(hwnd, true) fails. This creates a composition target bound to the given top-level window; the 'true' flag means topmost. Failure occurs when the HWND is invalid, not a top-level window, already has a composition target (from another device), or access is denied (different integrity level / UIPI).
Source
Thrown at src/renderer/d3d.rs:201
tracy_zone!("create swap_chain");
dxgi_factory
.CreateSwapChainForComposition(&command_queue, &swap_chain_desc, None)
.expect("Failed to create the Direct3D swap chain")
};
let swap_chain: IDXGISwapChain3 =
IDXGISwapChain1::cast(&swap_chain).expect("Failed to cast");
unsafe {
swap_chain.SetMaximumFrameLatency(1).expect("Failed to set maximum frame latency");
}
let composition_device: IDCompositionDevice = unsafe {
DCompositionCreateDevice2(None).expect("Could not create composition device")
};
let target = unsafe {
composition_device
.CreateTargetForHwnd(hwnd, true)
.expect("Could not create composition target")
};
let visual = unsafe {
composition_device.CreateVisual().expect("Could not create composition visual")
};
unsafe {
visual.SetContent(&swap_chain).expect("Failed to set composition content");
target.SetRoot(&visual).expect("Failed to set composition root");
composition_device.Commit().expect("Failed to commit composition");
}
let swap_chain_waitable = unsafe { swap_chain.GetFrameLatencyWaitableObject() };
if swap_chain_waitable.is_invalid() {
panic!("Failed to get swapchain waitable object");
}
// use a high value to make it easier to track these in PIX
let fence_values = vec![10000; swap_chain_desc.BufferCount as usize];View on GitHub (pinned to ade2d9cda7)
Solutions
- Ensure hwnd is a top-level window (use the winit outer window handle, not a child)
- Close/release any previous composition target for the same HWND before creating a new one
- Recreate the renderer rather than calling new twice on the same window
- Run the app at the same or higher integrity level than the window's owner
Defensive patterns
Strategy: validation
Validate before calling
fn is_top_level_window(hwnd: HWND) -> bool {
unsafe { GetAncestor(hwnd, GA_ROOT) == hwnd && IsWindow(hwnd).as_bool() }
}
// call before CreateTargetForHwnd
assert!(is_top_level_window(hwnd), "CreateTargetForHwnd needs a top-level HWND"); Try / catch
let target = composition_device.CreateTargetForHwnd(hwnd, true)
.map_err(|e| { log::error!("CreateTargetForHwnd({hwnd:?}) failed: {e}"); RendererInitError::Target(e) })?; Prevention
- Use the outer/top-level winit window handle, never a child HWND
- Only create one composition target per HWND per process; drop it before re-creating
- Match integrity levels (don't mix elevated/unelevated across process and window owner)
- Recreate the renderer instead of double-initializing the same window
When it happens
Trigger: HWND is child/control window instead of a top-level window; another IDCompositionDevice already created a target for the same HWND; hwnd handle invalid or belongs to another process with higher integrity; running elevated vs non-elevated mismatch.
Common situations: Passing a winit child window handle; two renderer instances for one window; mixed elevation (app elevated, window created unelevated or vice versa).
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Could not create composition device
- Could not create composition visual
- Failed to set composition content
- Failed to set composition root
- Failed to commit composition
AI-assisted analysis of neovide/neovide@ade2d9cda7 (2026-09-06).
Data as JSON: /api/errors/b81627fa95d5ab0e.
Report an issue: GitHub.