{"id":"ff3953c9e8f01026","repo":"seanmonstar/reqwest","slug":"timeout","errorCode":null,"errorMessage":"timeout","messagePattern":"timeout","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/wasm/mod.rs","lineNumber":70,"sourceCode":"        Ok(AbortGuard {\n            ctrl: AbortController::new()\n                .map_err(crate::error::wasm)\n                .map_err(crate::error::builder)?,\n            timeout: None,\n        })\n    }\n\n    fn signal(&self) -> AbortSignal {\n        self.ctrl.signal()\n    }\n\n    fn timeout(&mut self, timeout: Duration) {\n        let ctrl = self.ctrl.clone();\n        let abort =\n            Closure::once(move || ctrl.abort_with_reason(&\"reqwest::errors::TimedOut\".into()));\n        let timeout = set_timeout(\n            abort.as_ref().unchecked_ref::<js_sys::Function>(),\n            timeout.as_millis().try_into().expect(\"timeout\"),\n        );\n        if let Some((id, _)) = self.timeout.replace((timeout, abort)) {\n            clear_timeout(id);\n        }\n    }\n}\n\nimpl Drop for AbortGuard {\n    fn drop(&mut self) {\n        self.ctrl.abort();\n        if let Some((id, _)) = self.timeout.take() {\n            clear_timeout(id);\n        }\n    }\n}\n","sourceCodeStart":52,"sourceCodeEnd":86,"githubUrl":"https://github.com/seanmonstar/reqwest/blob/17e9bcb51c46edebfb6f5f2f5184b51dac4b3a7d/src/wasm/mod.rs#L52-L86","documentation":"This is a PANIC (not a returned `Error`) raised at wasm/mod.rs:70 by `.expect(\"timeout\")`. The wasm client converts the per-request `Duration` to milliseconds and narrows `u128 → i32` for the browser's `setTimeout`; if the millisecond count exceeds `i32::MAX` (~2,147,483,647 ms ≈ 24.8 days) the narrowing fails and the future's task panics.","triggerScenarios":"In a wasm target, calling `RequestBuilder::timeout(Duration::from_secs(N))` (or per-request timeout) where N is large enough that `as_millis() > i32::MAX` — e.g. `Duration::from_secs(u64::MAX)`, a 30-day timeout, or accidentally passing `Duration::MAX`.","commonSituations":"Loading timeout from config as a raw number of seconds and overflowing; using `Duration::MAX` as 'no timeout'; copy-paste producing an absurd duration in a wasm build that would be harmless on native.","solutions":["Cap the per-request timeout well below ~24.8 days (e.g. clamp to a sane max like an hour) before passing it to `.timeout()`.","Pass `None` (no `.timeout()` call) instead of a giant duration when you mean 'unlimited'.","If you must have very long deadlines, implement them in app logic rather than via the wasm `setTimeout` path."],"exampleFix":"// before (wasm)\nlet r = client.get(url).timeout(Duration::MAX).send().await?; // panics 'timeout'\n\n// after\nconst CAP: Duration = Duration::from_secs(3600);\nlet req = client.get(url);\nlet req = match maybe_timeout {\n    Some(d) if d <= CAP => req.timeout(d),\n    _ => req, // no timeout\n};\nlet r = req.send().await?;","handlingStrategy":"validation","validationCode":"const WASM_TIMEOUT_CAP: Duration = Duration::from_secs(3600);\nfn clamp_timeout(d: Duration) -> Option<Duration> {\n    (d <= WASM_TIMEOUT_CAP).then_some(d)\n}\n// usage on wasm\nlet req = client.get(url);\nlet req = maybe_timeout.and_then(clamp_timeout).map(|d| req.timeout(d)).unwrap_or(req);\n","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never pass Duration::MAX or huge durations to .timeout() in wasm; pass None instead.","Clamp config-driven timeouts to a sane ceiling (< 1 hour) before reaching reqwest.","Remember this is a panic, not an Err — it will unwind the wasm task unless caught by a panic hook."],"tags":["wasm","timeout","panic","overflow"],"analyzedSha":"17e9bcb51c46edebfb6f5f2f5184b51dac4b3a7d","analyzedAt":"2026-08-06T01:23:05.134Z","schemaVersion":2}