linebender/druid · error
Failed to request animation frame
Error message
Failed to request animation frame
What it means
Panic from `.expect()` when the JS `window.requestAnimationFrame()` call, made while building a `Window` in the web backend, throws or the browser rejects the callback registration. The library uses an animation frame to deliver the initial scale/size events to the handler; without it the new window would never learn its geometry, so failure is treated as fatal to `build`.
Solutions
- Ensure `build()` runs from a real browser window context after the DOM/canvas is ready (e.g. inside `requestAnimationFrame`/`onload`), not in a worker or during unload
- Provide a `requestAnimationFrame` polyfill when testing under Node/jsdom (e.g. raf polyfill package)
- Check the browser console for the underlying JS exception; fix the page lifecycle issue it reports
- Patch the app to call `build()` after `document.visibilitychange` indicates the page is visible
Example fix
// before
let window = WindowBuilder::new(app).build().expect("build failed"); // panics here in jsdom
// after (ensure browser-like env first)
if window web_sys::window().and_then(|w| w.request_animation_frame(cb)) .is_none() {
panic!("requestAnimationFrame unavailable: run in a real browser environment");
}
let window = WindowBuilder::new(app).build().expect("build failed"); Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure a real browser context exists before building windows
fn browser_ready() -> bool {
web_sys::window()
.map(|w| w.document().is_some())
.unwrap_or(false)
} Type guard
fn has_window() -> bool {
web_sys::window().is_some()
} Try / catch
// Panics cannot be caught across wasm normally; validate before build:
if !browser_ready() {
console_error("window.build called outside a browser window context");
return; // abort startup instead of panicking inside build()
}
let handle = WindowBuilder::new(app).build().expect("build failed"); Prevention
- Call build() only from browser UI events, never from workers or unload handlers
- Add a rAF polyfill when running wasm tests under Node/jsdom
- Wait for DOMContentLoaded/canvas mount before constructing windows
- Watch the browser console for JS exceptions during startup
When it happens
Trigger: `WindowBuilder::build()` (web/wasm32 target) when `requestAnimationFrame` throws — e.g. the page is being torn down, the code runs outside a browser window context (worker/jsdom/testing harness), or the browser refuses callback registration during page unload/navigation.
Common situations: Testing a druid/wasm app under Node or jsdom where `requestAnimationFrame` is unimplemented, embedding the canvas in an iframe that is removed during startup, or triggering window creation during `beforeunload`/`visibility: hidden` shutdown.
Related errors
- request_animation_frame failed
- 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/5a0e96f58b853bd0.
Report an issue: GitHub.
Appendix: source
Thrown at druid-shell/src/backend/web/window.rs:478
canvas,
canvas_size,
context,
invalid: RefCell::new(Region::EMPTY),
click_counter: ClickCounter::default(),
active_text_input: Cell::new(None),
rendering_soon: Cell::new(false),
});
setup_web_callbacks(&window);
// Register the scale & size with the window handler.
let wh = window.clone();
window
.request_animation_frame(move || {
wh.handler.borrow_mut().scale(scale);
wh.handler.borrow_mut().size(size_dp);
})
.expect("Failed to request animation frame");
let handle = WindowHandle(Rc::downgrade(&window));
window.handler.borrow_mut().connect(&handle.clone().into());
Ok(handle)
}
}
impl WindowHandle {
pub fn show(&self) {
self.render_soon();
}
pub fn resizable(&self, _resizable: bool) {
warn!("resizable unimplemented for web");
}
View on GitHub (pinned to 0f8b1195e4)