RightNow-AI/openfang · error · BrowserResponse

Click failed: {e}

Error message

Click failed: {e}

What it means

This is the CDP-transport-level click failure: the run_js call inside cmd_click returned Err, meaning the script never executed — the CDP connection itself failed rather than the click logic reporting failure. The inner CDP error is interpolated into the message.

Source

Thrown at crates/openfang-runtime/src/browser.rs:479

                    .and_then(|s| serde_json::from_str(s).ok())
                    .unwrap_or(val);
                if parsed["success"].as_bool() == Some(false) {
                    return BrowserResponse::err(
                        parsed["error"]
                            .as_str()
                            .unwrap_or("Click failed")
                            .to_string(),
                    );
                }
                // Wait briefly for any navigation triggered by click
                tokio::time::sleep(Duration::from_millis(500)).await;
                self.wait_for_load().await;
                match self.page_info().await {
                    Ok(info) => BrowserResponse::ok(info),
                    Err(_) => BrowserResponse::ok(parsed),
                }
            }
            Err(e) => BrowserResponse::err(format!("Click failed: {e}")),
        }
    }

    async fn cmd_type(&self, selector: &str, text: &str) -> BrowserResponse {
        let sel_json = serde_json::to_string(selector).unwrap_or_default();
        let text_json = serde_json::to_string(text).unwrap_or_default();
        let js = format!(
            r#"(() => {{
    let sel = {sel_json};
    let txt = {text_json};
    let el = document.querySelector(sel);
    if (!el) return JSON.stringify({{success: false, error: 'Input not found: ' + sel}});
    el.focus();
    el.value = txt;
    el.dispatchEvent(new Event('input', {{bubbles: true}}));
    el.dispatchEvent(new Event('change', {{bubbles: true}}));
    return JSON.stringify({{success: true, selector: sel, typed: txt.length + ' chars'}});
}})()"#

View on GitHub (pinned to acf2587e46)

Solutions

  1. Read the interpolated {e} for the underlying cause (disconnected, context destroyed, timeout).
  2. Reconnect or restart the browser session and retry the click.
  3. Re-navigate if the execution context was destroyed by an in-flight navigation.
  4. Wrap click in retry logic with a small backoff for transient CDP drops.

Example fix

// before
let resp = browser.execute("click", selector).await?;
// after
let resp = match browser.execute("click", selector).await {
    Ok(r) if r.success => r,
    Ok(r) => return Err(anyhow!(r.error)),
    Err(e) => {
        browser.reconnect().await?;
        browser.execute("click", selector).await?
    }
};
Defensive patterns

Strategy: retry

Validate before calling

// Check session liveness before clicking:
if !browser.is_connected().await { browser.reconnect().await?; }

Type guard

fn is_cdp_transport_err(e: &anyhow::Error) -> bool {
    let s = e.to_string();
    s.contains("connection") || s.contains("closed") || s.contains("context")
}

Try / catch

// Rust
match browser.execute("click", selector).await {
    Ok(r) if r.success => Ok(r),
    Ok(r) => Err(anyhow!(r.error)),
    Err(e) if is_cdp_transport_err(&e) => {
        browser.reconnect().await?;
        browser.execute("click", selector).await.map_err(Into::into)
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: self.cdp.run_js(click_script) resolves to Err: browser disconnected, target/context closed, evaluation timed out, or the CDP session died between navigate and click.

Common situations: Headless Chrome crashed or was killed between commands; navigation destroyed the execution context while the click JS was being evaluated; CDP websocket dropped.

Related errors


AI-assisted analysis of RightNow-AI/openfang@acf2587e46 (2026-09-02). Data as JSON: /api/errors/641f24f7c39ce927. Report an issue: GitHub.