RightNow-AI/openfang · error · BrowserResponse

Click failed

Error message

Click failed

What it means

cmd_click evaluates a JS snippet via CDP that clicks the element matching a selector and returns {success, error} JSON. When the script reports success=false, this fallback message 'Click failed' is used if the script did not include an 'error' string — i.e. the click script itself determined the click could not be performed (element not found, not clickable).

Solutions

  1. Verify the selector matches exactly one visible element (test it in the browser console with document.querySelector).
  2. Use cmd_wait on the selector before clicking to ensure it exists in the DOM.
  3. Inspect the parsed error payload — the script usually returns a more specific 'error' field than this fallback.
  4. For shadow DOM or iframes, use a selector strategy that penetrates them or switch frames first.

Example fix

// before
let _ = browser.execute("click", selector).await?;
// after
browser.execute("wait", &format!("{selector}|5000")).await?;
let resp = browser.execute("click", selector).await?;
if !resp.success { return Err(anyhow!(resp.error)); }
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the element exists before clicking:
let wait = browser.execute("wait", &format!("{selector}|5000")).await?;
if !wait.success { return Err(anyhow!("element not present: {selector}")); }

Type guard

fn click_failed_no_reason(resp: &BrowserResponse) -> bool {
    !resp.success && resp.error == "Click failed"
}

Try / catch

// Rust
let resp = browser.execute("click", selector).await?;
if !resp.success {
    if resp.error == "Click failed" {
        // no script-side reason — most likely selector miss; wait and retry once
        browser.execute("wait", &format!("{selector}|3000")).await?;
        return browser.execute("click", selector).await.map_err(Into::into);
    }
    return Err(anyhow!(resp.error));
}
Ok(resp)

Prevention

When it happens

Trigger: Running the click JS over CDP returns a value whose parsed["success"] is false and parsed["error"] is missing or not a string — e.g. the injected script failed to find the selector and returned success:false without a message.

Common situations: Selector typo or element rendered client-side after the click attempt; element inside an iframe the script doesn't reach; stale selector after a re-render.

Related errors


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

Appendix: source

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

        for (const e of all) {{
            if (e.textContent.trim().toLowerCase().includes(lower)) {{ el = e; break; }}
        }}
    }}
    if (!el) return JSON.stringify({{success: false, error: 'Element not found: ' + sel}});
    el.scrollIntoView({{block: 'center'}});
    el.click();
    return JSON.stringify({{success: true, tag: el.tagName, text: el.textContent.substring(0, 100).trim()}});
}})()"#
        );

        match self.cdp.run_js(&js).await {
            Ok(val) => {
                let parsed: serde_json::Value = val
                    .as_str()
                    .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}")),
        }
    }

View on GitHub (pinned to acf2587e46)