herdrdev/herdr · warning · io::Error
Windows notification setup timed out
Error message
Windows notification setup timed out
What it means
Showing a Windows desktop notification happens on a dedicated thread that signals readiness over a channel. If the thread does not report readiness within 2 seconds, this TimedOut error is returned so callers do not block indefinitely on a stuck WinRT/notification setup.
Source
Thrown at src/platform/windows.rs:2001
let mut bytes = vec![0_u8; size];
unsafe {
copy_nonoverlapping(data.cast::<u8>(), bytes.as_mut_ptr(), size);
GlobalUnlock(handle);
}
Some(bytes)
}
pub fn show_desktop_notification(title: &str, body: Option<&str>) -> std::io::Result<bool> {
let title = title.to_owned();
let body = body.unwrap_or(&title).to_owned();
let (ready_tx, ready_rx) = std::sync::mpsc::sync_channel(1);
std::thread::Builder::new()
.name("herdr-windows-notification".into())
.spawn(move || show_desktop_notification_on_thread(&title, &body, ready_tx))?;
ready_rx
.recv_timeout(Duration::from_secs(2))
.map_err(|err| match err {
std::sync::mpsc::RecvTimeoutError::Timeout => std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Windows notification setup timed out",
),
std::sync::mpsc::RecvTimeoutError::Disconnected => std::io::Error::other(
"Windows notification thread exited before reporting readiness",
),
})?
}
fn show_desktop_notification_on_thread(
title: &str,
body: &str,
ready_tx: std::sync::mpsc::SyncSender<std::io::Result<bool>>,
) {
let class_name = wide_null("STATIC");
let window_name = wide_null("Herdr notifications");
let hwnd = unsafe {
CreateWindowExW(View on GitHub (pinned to f457cff4f2)
Solutions
- Retry the notification; transient setup stalls under load usually succeed on a second attempt
- Check that Windows notifications are enabled for the app and WpnUserService/WpnService is running
- If running over SSH or as a service, ensure an interactive desktop session exists — WinRT toast needs one
- Report/inspect Herdr logs for the companion 'thread exited before reporting readiness' error to distinguish a crash from a stall
Defensive patterns
Strategy: retry
Try / catch
match show_desktop_notification(title, body) {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
// one bounded retry; notifications are best-effort
let _ = show_desktop_notification(title, body);
}
Err(_) => {} // never fail the user flow on a notification
} Prevention
- Treat notifications as best-effort: log failures, never propagate them to the UI flow
- Bound retries to one or two attempts to avoid notification storms on a stuck system
- Check WpnService health and interactive-session availability when the error repeats
- Distinguish Timeout from the Disconnected 'thread exited' error — the latter indicates a crash, not a stall
When it happens
Trigger: Invoking show_desktop_notification on Windows when the spawned notification thread takes longer than the 2-second recv_timeout to initialize (e.g. COM/WinRT initialization stall, system under load, notification service unresponsive).
Common situations: Heavily loaded machines, notification service (WpnService) issues, first COM apartment initialization in a constrained session (SSH/service context without an interactive desktop), or a hung Windows notification API during startup.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- timed out reading api request
- timed out waiting for app response after {} ms
- timed out reading stream frame header
- timed out reading stream frame body
- failed to {operation} managed plugin checkout at {}; close a
AI-assisted analysis of herdrdev/herdr@f457cff4f2 (2026-08-28).
Data as JSON: /api/errors/ac00fe30d2a6567f.
Report an issue: GitHub.