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
- Re-check the target is alive (query /json list) before capturing
- Retry the capture; transient navigation races often clear
- Verify CDP command parameters (format, clip rect within viewport)
- 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
- Verify the target still exists before sending Page.captureScreenshot
- Use a stable page-load signal (Page.loadEventFired) before capture
- Log the raw CDP response when data is missing
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
- browser WebSocket URL has no path
- CDP WebSocket URL must include an explicit port
- Page.captureScreenshot returned invalid PNG data
- browser WebSocket URL has no Browser ID
- CDP command id failed
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)