libnyanpasu/clash-nyanpasu · error
Failed to get module handle: {err}
Error message
Failed to get module handle: {err} What it means
On Windows, the shutdown-hook thread needs the module handle of the running executable (GetModuleHandleW(null)) to register a window class for the hidden message-only window that receives WM_QUERYENDSESSION/console signals. If GetModuleHandleW returns null, the code converts the last OS error via Error::from_win32() and bails with this message, so hook setup fails before the window is created.
Source
Thrown at backend/tauri/src/shutdown_hook.rs:128
fn setup_shutdown_hook_inner(
f: impl Fn() + Send + Sync + 'static,
initd_tx: oneshot::Sender<()>,
) -> anyhow::Result<()> {
let class_name = w!("TAURI_SHUTDOWN_HOOK");
let (tx, rx) = std::sync::mpsc::channel::<()>();
std::thread::spawn(move || {
while let Ok(()) = rx.recv() {
f();
}
});
SHUTDOWN_HOOK_INSTANCE.set(tx).unwrap();
let h_instance = unsafe { GetModuleHandleW(std::ptr::null()) };
if h_instance.is_null() {
let err = Error::from_win32();
anyhow::bail!("Failed to get module handle: {err}");
}
let mut window_class_ex = unsafe { std::mem::zeroed::<WNDCLASSEXW>() };
window_class_ex.cbSize = std::mem::size_of::<WNDCLASSEXW>() as u32;
window_class_ex.lpszClassName = class_name.as_ptr();
window_class_ex.lpfnWndProc = Some(callback);
window_class_ex.hInstance = h_instance;
unsafe {
if RegisterClassExW(&window_class_ex) == 0 {
let err = Error::from_win32();
anyhow::bail!("Failed to register window class: {err}");
}
}
let window_name = w!("TAURI_SHUTDOWN_HOOK_WINDOW");
let hidden_window = unsafe {
CreateWindowExW(View on GitHub (pinned to f7dbce2997)
Solutions
- Inspect the Win32 error embedded in the message (err) and address its cause (permissions, host environment, resource limits)
- Run the app as a normal user-launched process rather than inside a restrictive service host if signal handling via window messages is required
- Add a fallback shutdown path (e.g. console ctrl handler via SetConsoleCtrlHandler) if GetModuleHandleW fails, instead of hard-failing setup
- Log the error and continue app startup, treating graceful-signal shutdown as degraded (best-effort) rather than fatal
Example fix
// before: hard failure on missing module handle
if h_instance.is_null() {
let err = Error::from_win32();
anyhow::bail!("Failed to get module handle: {err}");
}
// after: degrade gracefully, keep app running without signal window
if h_instance.is_null() {
let err = Error::from_win32();
log::warn!("shutdown signal window unavailable: {err}");
return Ok(());
} Defensive patterns
Strategy: try-catch
Try / catch
if let Err(e) = setup_shutdown_hook(on_shutdown) {
if e.to_string().starts_with("Failed to get module handle") {
log::warn!("graceful shutdown signals unavailable: {e}; continuing");
} else {
return Err(e);
}
} Prevention
- Run the app as a normally launched user process, not inside a restrictive service host
- Treat graceful signal handling as best-effort: degrade to normal exit if the Win32 setup fails
- Include the Win32 error code in logs to diagnose host/permission issues quickly
- Test hook setup on target Windows environments (service, CI, standard user) before release
When it happens
Trigger: Calling setup_shutdown_hook on Windows when GetModuleHandleW(std::ptr::null()) fails — e.g. the process image cannot be resolved (corrupted/unusual host process, limited execution context such as a service or embedded host where the module is not accessible), or Win32 returns an unexpected error at startup.
Common situations: Running the app inside an unusual Windows host (service host, non-standard launcher) where the executable module handle is unavailable; Windows API failures due to low resources or security software blocking module enumeration; running under constrained CI/service accounts.
Related errors
- Shutdown hook already set
- Failed to register window class: {err}
- Failed to create hidden window: {err}
- register() called before prepare()
- listen() called before prepare()
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/a27e4afe6eaca2b2.
Report an issue: GitHub.