BigPizzaV3/CodexPlusPlus · error

Page.captureScreenshot returned no image data

Error message

Page.captureScreenshot returned no image data

What it means

Raised by `capture_page_screenshot` when the CDP `Page.captureScreenshot` response arrives but contains no usable image payload. The code drills into `result.data`, requires it to be a non-empty string, and fails with this message otherwise. It means Chrome acknowledged the command but returned no base64 screenshot data.

Solutions

  1. Re-check the target is alive (query /json list) before capturing
  2. Retry the capture; transient navigation races often clear
  3. Verify CDP command parameters (format, clip rect within viewport)
  4. Log the full CDP response when data is missing to distinguish 'no data' from 'wrong shape'

Example fix

// before
let encoded = response.get("result")?.get("data")?.as_str()?...;
// after
let result = response.get("result")
    .ok_or_else(|| anyhow::anyhow!("captureScreenshot: no result: {response:?}"))?;
let encoded = result.get("data").and_then(Value::as_str)
    .filter(|d| !d.is_empty())
    .ok_or_else(|| anyhow::anyhow!("Page.captureScreenshot returned no image data (result={result:?})"))?;
Defensive patterns

Strategy: retry

Validate before calling

// ensure target is alive before capture
let targets: Vec<_> = serde_json::from_str(&http_get(format!("http://127.0.0.1:{port}/json/list"))?)?;
assert!(targets.iter().any(|t| t["id"] == target_id));

Try / catch

match capture_page_screenshot(...).await {
    Err(e) if e.to_string().contains("no image data") => {
        tokio::time::sleep(Duration::from_millis(300)).await;
        retry_capture(2)
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling `capture_page_screenshot` against a target whose response JSON lacks `result.data`, has `data: null`, or an empty string — e.g. page crashed, target closed mid-capture, or capture parameters (clip/viewport) invalid so Chrome returned an empty result.

Common situations: Screenshotting a tab that was just closed or navigated, capturing a crashed renderer (out-of-memory page), or an old/headless Chrome version returning a different response shape.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/3239622208c69a9e. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/bridge.rs:206

        .await
}

pub async fn capture_page_screenshot(
    websocket_url: &str,
    output_path: &Path,
) -> anyhow::Result<u64> {
    let response = send_cdp_command(
        websocket_url,
        "Page.captureScreenshot",
        capture_screenshot_params(),
    )
    .await?;
    let encoded = response
        .get("result")
        .and_then(|result| result.get("data"))
        .and_then(Value::as_str)
        .filter(|data| !data.is_empty())
        .ok_or_else(|| anyhow::anyhow!("Page.captureScreenshot returned no image data"))?;
    let bytes = base64::engine::general_purpose::STANDARD
        .decode(encoded)
        .context("failed to decode screenshot PNG")?;
    if !bytes.starts_with(&[137, 80, 78, 71, 13, 10, 26, 10]) {
        bail!("Page.captureScreenshot returned invalid PNG data");
    }
    crate::settings::atomic_write(output_path, &bytes)
        .with_context(|| format!("failed to save screenshot {}", output_path.display()))?;
    Ok(bytes.len() as u64)
}

pub async fn run_periodic_evaluations<F>(
    websocket_url: &str,
    period: Duration,
    mut next_expression: F,
) -> anyhow::Result<()>
where
    F: FnMut() -> anyhow::Result<Option<String>>,

View on GitHub (pinned to b1ed92e5e4)