linebender/druid · error
request_animation_frame failed
Error message
request_animation_frame failed
What it means
Panic from `.expect()` when `IdleHandle::wake` (or the equivalent idle-notification path) fails to schedule `process_idle_queue` via `request_animation_frame`. Idle tokens are flushed on the next animation frame; if the browser rejects the rAF registration, queued idle work would be silently lost, so the library treats it as fatal.
Solutions
- Drop/cancel IdleHandles and background tasks when the window closes instead of waking a dead window
- Make background completions check page/window liveness (e.g. `web_sys::window().is_some()`) before scheduling idle work
- Add a requestAnimationFrame polyfill for non-browser test environments
- Debounce wake-ups so many late callbacks don't pile up on a torn-down page
Example fix
// before
handle.wake(); // panics if page unloaded
// after
if web_sys::window().is_some() {
handle.wake();
} Defensive patterns
Strategy: fallback
Validate before calling
// Confirm browser context before waking idle handles
fn safe_to_wake() -> bool {
web_sys::window().is_some()
} Type guard
fn handle_live(handle: &IdleHandle) -> bool {
!handle_window_closed && web_sys::window().is_some()
} Try / catch
// Fallback: retry once on the next tick, else log and drop
if safe_to_wake() {
handle.wake();
} else {
console_error("idle wake dropped: no window context");
} Prevention
- Cancel or drop IdleHandles on window close/navigation
- Route all background completions through a liveness-checked scheduler
- Use requestIdleCallback/rAF polyfills in tests
- Debounce idle wake-ups to reduce late callbacks on torn-down pages
When it happens
Trigger: `IdleHandle::wake` / `schedule_idle` on the web backend after the window has been dropped (the `upgrade()` still succeeds but the page is torn down), during page unload, or in an environment lacking `requestAnimationFrame`.
Common situations: A background task (fetch promise, WebSocket message) resolves and calls `schedule_idle` after the user navigated away or closed the tab; running wasm under Node/jsdom tests; detached iframes receiving late idle wake-ups.
Related errors
- Failed to request animation frame
- Failed to produce a text context
- Failed to call setTimeout with a callback
AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10).
Data as JSON: /api/errors/9c9bb0c0929b3e00.
Report an issue: GitHub.
Appendix: source
Thrown at druid-shell/src/backend/web/window.rs:741
unsafe impl Sync for IdleHandle {}
impl IdleHandle {
/// Add an idle handler, which is called (once) when the main thread is idle.
pub fn add_idle_callback<F>(&self, callback: F)
where
F: FnOnce(&mut dyn WinHandler) + Send + 'static,
{
let mut queue = self.queue.lock().expect("IdleHandle::add_idle queue");
queue.push(IdleKind::Callback(Box::new(callback)));
if queue.len() == 1 {
if let Some(window_state) = self.state.upgrade() {
let state = window_state.clone();
window_state
.request_animation_frame(move || {
state.process_idle_queue();
})
.expect("request_animation_frame failed");
}
}
}
pub fn add_idle_token(&self, token: IdleToken) {
let mut queue = self.queue.lock().expect("IdleHandle::add_idle queue");
queue.push(IdleKind::Token(token));
if queue.len() == 1 {
if let Some(window_state) = self.state.upgrade() {
let state = window_state.clone();
window_state
.request_animation_frame(move || {
state.process_idle_queue();
})
.expect("request_animation_frame failed");
}
}View on GitHub (pinned to 0f8b1195e4)