BigPizzaV3/CodexPlusPlus · error · anyhow::Error
Page.captureScreenshot returned no image data
Error message
Page.captureScreenshot returned no image data
What it means
capture_screenshot (crates/codex-plus-core/src/bridge.rs:130) sends the CDP command Page.captureScreenshot over the debug websocket and expects response.result.data to be a non-empty base64 string. If the field is missing, null, not a string, or an empty string, this error is raised before the base64/PNG decoding stages. It means the browser answered but attached no screenshot payload — typically an empty/error-shaped CDP response rather than a transport failure.
Source
Thrown at crates/codex-plus-core/src/bridge.rs:130
.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 1f431ae49b)
Solutions
- Retry once after a short delay — transient during navigation/first paint, and the helper is called in a periodic loop where the next tick usually succeeds
- Verify the selected target is a real injectable page (pick_page_target) and still alive before capturing
- Log the full CDP response when data is missing: an "error" object in it (e.g. 'Cannot take screenshot with 0 width') pinpoints the real browser-side cause
- Ensure the window/page has non-zero visible size (not minimized to 0x0) when capturing from a headed browser
Example fix
// before: single-shot capture fails when the page is mid-navigation
let size = capture_screenshot(ws_url, &out).await?;
// after: tolerate one transient empty-payload response
let size = match capture_screenshot(ws_url, &out).await {
Ok(size) => size,
Err(e) if e.to_string().contains("no image data") => {
tokio::time::sleep(Duration::from_millis(500)).await;
capture_screenshot(ws_url, &out).await?
}
Err(e) => return Err(e),
}; Defensive patterns
Strategy: retry
Validate before calling
// Before capturing: confirm the page target is alive and injectable let targets = list_targets(port).await?; let page = pick_page_target(&targets)?; // fails fast on dead/non-page targets // ensure non-blank state, e.g. wait for first load: crate::bridge::wait_for_page_ready(&ws_url).await?;
Try / catch
// Transient during navigation/first paint — retry once with backoff
let mut last = None;
for attempt in 0..2 {
match capture_screenshot(ws_url, &out).await {
Ok(n) => return Ok(n),
Err(e) if e.to_string().contains("no image data") => {
tokio::time::sleep(std::time::Duration::from_millis(500 * (attempt + 1))).await;
last = Some(e);
}
Err(e) => return Err(e),
}
}
Err(last.unwrap()) Prevention
- Capture only after the target finishes loading (Page.loadEventFired or equivalent)
- Skip capture when the page target is mid-navigation or was just detached
- Keep the browser window with a non-zero size when capturing headed
- Log the raw CDP response when data is missing — an error object names the browser-side cause
When it happens
Trigger: Calling the screenshot path against a page target that just navigated, crashed, or was closed before Page.captureScreenshot completed; capturing from a non-renderable target (a background/blank page or one where the compositor produced nothing); a CDP error response ({"error":...}) whose result member is absent, so the data lookup fails; very early capture before the first paint on a freshly opened page.
Common situations: Periodic evaluation (run_periodic_evaluations) firing while the page is mid-navigation or being torn down; headless/new-mode Chrome returning an empty data field for about:blank; browser version change altering capture behavior; DevTools/CDP session taken over by another client that detached the target.
Related errors
- CDP WebSocket URL must include an explicit port
- CDP WebSocket URL has no host
- CDP WebSocket URL must include an explicit port
- selected CDP target has no websocket URL
- browser WebSocket URL has no path
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16).
Data as JSON: /api/errors/3239622208c69a9e.
Report an issue: GitHub.