{"record":{"id":"efb20fdea0cd2048","repo":"linebender/druid","slug":"failed-to-call-settimeout-with-a-callback","errorCode":null,"errorMessage":"Failed to call setTimeout with a callback","messagePattern":"Failed to call setTimeout with a callback","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"druid-shell/src/backend/web/window.rs","lineNumber":641,"sourceCode":"            }\n        };\n\n        let token = TimerToken::next();\n\n        if let Some(state) = self.0.upgrade() {\n            let s = state.clone();\n            let f = move || {\n                if let Ok(mut handler_borrow) = s.handler.try_borrow_mut() {\n                    handler_borrow.timer(token);\n                }\n            };\n            state\n                .window\n                .set_timeout_with_callback_and_timeout_and_arguments_0(\n                    Closure::once_into_js(f).as_ref().unchecked_ref(),\n                    interval,\n                )\n                .expect(\"Failed to call setTimeout with a callback\");\n        }\n        token\n    }\n\n    pub fn set_cursor(&mut self, cursor: &Cursor) {\n        if let Some(s) = self.0.upgrade() {\n            set_cursor(&s.canvas, cursor);\n        }\n    }\n\n    pub fn make_cursor(&self, _cursor_desc: &CursorDesc) -> Option<Cursor> {\n        warn!(\"Custom cursors are not yet supported in the web backend\");\n        None\n    }\n\n    pub fn open_file(&mut self, _options: FileDialogOptions) -> Option<FileDialogToken> {\n        warn!(\"open_file is currently unimplemented for web.\");\n        None","sourceCodeStart":623,"sourceCodeEnd":659,"githubUrl":"https://github.com/linebender/druid/blob/0f8b1195e4e073f9597f2865299c3d18f8e4005f/druid-shell/src/backend/web/window.rs#L623-L659","documentation":"Panic from `.expect()` when `set_timeout_with_callback_and_timeout_and_arguments_0` — the wasm-bindgen binding for JS `setTimeout` — throws inside `Window::request_timer`. The browser rejects `setTimeout` calls from a context that is being destroyed or in restricted environments, and since druid-shell's timer tokens rely on the callback being scheduled, failure is fatal.","triggerScenarios":"Calling `ExtEventSink::request_timer` / `Window::request_timer` (web backend) while the page is unloading, from a context where `setTimeout` is unavailable (worker, jsdom without timers), or with an `interval` the browser rejects (e.g. non-finite value).","commonSituations":"Scheduling a timer from an event sink after window close was requested, running wasm tests under Node with incomplete DOM timer shims, or very large/NaN timeout values reaching the JS layer.","solutions":["Check the page is still alive before requesting timers; don't request timers from event handlers racing window close/navigation","Validate the timeout value is a finite, non-negative number (NaN/undefined throws in the JS binding)","Supply complete timer polyfills when running under Node/jsdom test harnesses","Inspect the browser console for the underlying JS exception and fix the page lifecycle problem"],"exampleFix":"// before\nlet token = sink.request_timer(Duration::from_secs(1), None); // panics after window closed\n// after: only request timers while the window is still live\nif !window_closing.load(Ordering::SeqCst) {\n    let token = sink.request_timer(Duration::from_secs(1), None);\n}","handlingStrategy":"validation","validationCode":"// Validate the timer interval before requesting it\nfn valid_interval(d: std::time::Duration) -> bool {\n    d.as_millis() <= i32::MAX as u128\n}","typeGuard":"fn page_alive() -> bool {\n    web_sys::window().is_some()\n}","tryCatchPattern":"// Guard timer requests at the call site:\nif page_alive() && valid_interval(interval) {\n    sink.request_timer(interval, None);\n} else {\n    log::warn!(\"skipping timer request: page dead or invalid interval\");\n}","preventionTips":["Never request timers from code racing window close or navigation","Clamp durations to the browser's safe setTimeout range (< 2^31 ms)","Stop async tasks when the window closes so they can't schedule timers","Polyfill setTimeout fully in test harnesses"],"tags":["web","wasm","settimeout","timer"],"backgroundTag":"request-animation-frame-failed","analyzedSha":"0f8b1195e4e073f9597f2865299c3d18f8e4005f","analyzedAt":"2026-09-10T14:27:34.582Z","contentChangedAt":"2026-09-10T14:27:34.582Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}