linebender/druid · critical
Error registering class
Error message
Error registering class
What it means
During windows backend application initialization (Application::init), the win32 window class is registered with RegisterClassW. A return value of 0 means class registration failed, and the code panics with this message. Common win32 error codes behind it are ERROR_CLASS_ALREADY_EXISTS (1410) and access/parameter errors.
Solutions
- Ensure Application::init is called exactly once per process; guard with std::sync::Once if initialization may race.
- Call win32 GetLastError / FormatMessage around the failure (or wrap RegisterClassW) to see the actual error code and confirm whether it's ERROR_CLASS_ALREADY_EXISTS.
- If double-registration is intentional in tests, ignore error 1410 instead of panicking, or register with a unique class name per process.
- Run the app in an interactive user session rather than a service/sandboxed context, and check that no DLL has already registered the same class name.
Example fix
// before
let class_atom = unsafe { RegisterClassW(&wnd) };
if class_atom == 0 {
panic!("Error registering class");
}
// after: tolerate double init
let class_atom = unsafe { RegisterClassW(&wnd) };
if class_atom == 0 {
let err = unsafe { GetLastError() };
if err != ERROR_CLASS_ALREADY_EXISTS {
panic!("Error registering class: {err}");
}
} Defensive patterns
Strategy: validation
Validate before calling
// before init, verify class not already registered and single init use std::sync::Once; static INIT: Once = Once::new(); INIT.call_once(|| Application::new().unwrap());
Try / catch
// init panics; run it in a check that tolerates double-init
std::panic::catch_unwind(Application::init)
.map_err(|_| anyhow!("Application::init failed (already initialized or restricted session)"))?; Prevention
- Call Application::init/new exactly once per process (use std::sync::Once).
- Don't run druid apps inside services, AppContainers, or restricted sessions without verifying window-class registration rights.
- Avoid third-party DLLs that may register a conflicting class name.
- Capture GetLastError around win32 calls when debugging startup failures.
When it happens
Trigger: Calling Application::init (or new()) twice in the same process so the same class name is registered twice; RegisterClassW failing due to an invalid WNDCLASSW field or the process lacking rights to register classes (restricted desktop/session, some service or sandboxed environments).
Common situations: Unit tests or examples that initialize the druid-shell Application more than once per process; running under restricted environments (Windows Service Session 0, some CI runners, AppContainer) where class registration is denied; conflicts from another framework registering an identically named class.
Related errors
- Unwrap named called on unnamed FieldIdent
- Application is already running
- Application state already borrowed
- unexpected wayland event
- unrecognised key event
AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10).
Data as JSON: /api/errors/dbfd81951568b17f.
Report an issue: GitHub.
Appendix: source
Thrown at druid-shell/src/backend/windows/application.rs:98
.is_ok()
{
let class_name = CLASS_NAME.to_wide();
let icon = unsafe { LoadIconW(GetModuleHandleW(0 as LPCWSTR), MAKEINTRESOURCEW(1)) };
let wnd = WNDCLASSW {
style: 0,
lpfnWndProc: Some(window::win_proc_dispatch),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: 0 as HINSTANCE,
hIcon: icon,
hCursor: 0 as HCURSOR,
hbrBackground: ptr::null_mut(), // We control all the painting
lpszMenuName: 0 as LPCWSTR,
lpszClassName: class_name.as_ptr(),
};
let class_atom = unsafe { RegisterClassW(&wnd) };
if class_atom == 0 {
panic!("Error registering class");
}
}
Ok(())
}
pub fn add_window(&self, hwnd: HWND) -> bool {
self.state.borrow_mut().windows.insert(hwnd)
}
pub fn remove_window(&self, hwnd: HWND) -> bool {
self.state.borrow_mut().windows.remove(&hwnd)
}
pub fn run(self, _handler: Option<Box<dyn AppHandler>>) {
unsafe {
// Handle windows messages.
//
// NOTE: Code here will not run when we aren't in charge of the message loop. ThatView on GitHub (pinned to 0f8b1195e4)