{"record":{"id":"163b7ee63e8c25ff","repo":"RightNow-AI/openfang","slug":"scroll-failed-e","errorCode":null,"errorMessage":"Scroll failed: {e}","messagePattern":"Scroll failed: (.+?)","errorType":"error_code","errorClass":"BrowserResponse","httpStatus":null,"severity":"error","filePath":"crates/openfang-runtime/src/browser.rs","lineNumber":572,"sourceCode":"\n    async fn cmd_scroll(&self, direction: &str, amount: i32) -> BrowserResponse {\n        let (dx, dy) = match direction {\n            \"up\" => (0, -amount),\n            \"down\" => (0, amount),\n            \"left\" => (-amount, 0),\n            \"right\" => (amount, 0),\n            _ => (0, amount),\n        };\n        let js = format!(\"window.scrollBy({dx}, {dy}); JSON.stringify({{scrollX: window.scrollX, scrollY: window.scrollY}})\");\n        match self.cdp.run_js(&js).await {\n            Ok(val) => {\n                let parsed: serde_json::Value = val\n                    .as_str()\n                    .and_then(|s| serde_json::from_str(s).ok())\n                    .unwrap_or(val);\n                BrowserResponse::ok(parsed)\n            }\n            Err(e) => BrowserResponse::err(format!(\"Scroll failed: {e}\")),\n        }\n    }\n\n    async fn cmd_wait(&self, selector: &str, timeout_ms: u64) -> BrowserResponse {\n        let sel_json = serde_json::to_string(selector).unwrap_or_default();\n        let max_ms = timeout_ms.min(30_000);\n        let polls = (max_ms / PAGE_LOAD_POLL_INTERVAL_MS).max(1);\n\n        for _ in 0..polls {\n            let js = format!(\"document.querySelector({sel_json}) ? 'found' : null\");\n            if let Ok(val) = self.cdp.run_js(&js).await {\n                if val.as_str() == Some(\"found\") {\n                    return BrowserResponse::ok(\n                        serde_json::json!({\"found\": true, \"selector\": selector}),\n                    );\n                }\n            }\n            tokio::time::sleep(Duration::from_millis(PAGE_LOAD_POLL_INTERVAL_MS)).await;","sourceCodeStart":554,"sourceCodeEnd":590,"githubUrl":"https://github.com/RightNow-AI/openfang/blob/acf2587e46be174c10200489c9a2d23a39a98aeb/crates/openfang-runtime/src/browser.rs#L554-L590","documentation":"cmd_scroll translates a direction/amount into dx/dy and evaluates scroll JS over CDP. When the CDP call returns Err, this message wraps the transport error — the scroll command never executed, so the viewport position is unchanged.","triggerScenarios":"The scroll JS sent via self.cdp.run_js resolves to Err: browser disconnected, execution context destroyed, or CDP evaluation failed/timed out.","commonSituations":"Scrolling during an in-flight navigation; headless browser crashed; CDP session dropped between commands; invalid direction/amount causing downstream JS issues in modified setups.","solutions":["Read the interpolated {e} for the root CDP cause.","Reconnect or restart the browser session and retry the scroll.","Avoid interleaving scroll with navigation; wait_for_load before scrolling.","Add retry-with-backoff for transient CDP transport errors."],"exampleFix":"// before\nlet resp = browser.execute(\"scroll\", &format!(\"{direction}|{amount}\")).await?;\n// after\nbrowser.wait_for_load().await?;\nlet resp = match browser.execute(\"scroll\", &format!(\"{direction}|{amount}\")).await {\n    Ok(r) => r,\n    Err(_) => {\n        browser.reconnect().await?;\n        browser.execute(\"scroll\", &format!(\"{direction}|{amount}\")).await?\n    }\n};","handlingStrategy":"retry","validationCode":"// Validate direction and settle the page before scrolling:\nconst DIRS: [&str; 4] = [\"up\", \"down\", \"left\", \"right\"];\nif !DIRS.contains(&direction) { return Err(anyhow!(\"bad direction: {direction}\")); }\nbrowser.wait_for_load().await?;","typeGuard":"fn is_cdp_transport_err(e: &anyhow::Error) -> bool {\n    let s = e.to_string();\n    s.contains(\"connection\") || s.contains(\"closed\") || s.contains(\"context\")\n}","tryCatchPattern":"// Rust\nmatch browser.execute(\"scroll\", &format!(\"{direction}|{amount}\")).await {\n    Ok(r) if r.success => Ok(r),\n    Ok(r) => Err(anyhow!(r.error)),\n    Err(e) if is_cdp_transport_err(&e) => {\n        browser.reconnect().await?;\n        browser.execute(\"scroll\", &format!(\"{direction}|{amount}\")).await.map_err(Into::into)\n    }\n    Err(e) => Err(e.into()),\n}","preventionTips":["Restrict direction to up/down/left/right and clamp amount.","Wait for load before scrolling to avoid context destruction.","Reconnect the session on transport errors and retry once.","Sequence scroll commands; avoid concurrent navigation."],"tags":["cdp","scroll","browser-automation"],"backgroundTag":"cdp-command-failed","analyzedSha":"acf2587e46be174c10200489c9a2d23a39a98aeb","analyzedAt":"2026-09-02T22:42:28.464Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}