BigPizzaV3/CodexPlusPlus · error · anyhow::Error
Runtime.evaluate raised an exception
Error message
Runtime.evaluate raised an exception: {exception} What it means
ensure_runtime_evaluate_succeeded checks Runtime.evaluate responses for result.exceptionDetails and, if JavaScript execution threw, bails with the serialized exception details. It is used both by direct script evaluation (evaluate_script_with_await_promise) and by periodic probes, so any injected script error surfaces here with the full CDP exception object in the message.
Solutions
- Parse the exceptionDetails in the message — it contains exception description, line/column, and stack trace
- Run the script manually in the target's DevTools console to reproduce and fix the JS error
- Guard the script against missing globals (typeof checks) so it degrades instead of throwing
- Ensure evaluation happens on a live, fully-loaded document; re-inject via add_script_to_new_documents after updates
Example fix
// before return window.__cap.doWork(); // after if (typeof window.__cap === 'undefined') return null; return window.__cap.doWork();
Defensive patterns
Strategy: try-catch
Validate before calling
// check the globals your script depends on exist before evaluating
let probe = evaluate_script(&ws, "return typeof window.__cap").await?;
if probe.as_str() != Some("object") { eprintln!("__cap not injected yet"); } Type guard
fn has_exception_details(response: &serde_json::Value) -> bool {
response.get("result").and_then(|r| r.get("exceptionDetails")).is_some()
} Try / catch
match evaluate_script(&ws, script).await {
Err(e) if e.to_string().contains("raised an exception") => {
// exceptionDetails in the message has description + stack; fix the script and retry
eprintln!("script threw: {e}");
}
other => other?,
} Prevention
- Use typeof guards in injected scripts so missing internals degrade to null instead of throwing
- Test scripts in the target's DevTools console against the exact app version
- Ensure the page is loaded and the execution context is live before Runtime.evaluate; re-inject on new documents
When it happens
Trigger: The evaluated JavaScript throws: referencing a undefined global (e.g. window.__codexPlusAppServerClientClass missing), a syntax error in the script, an unhandled promise rejection with awaitPromise:true, or the target context being destroyed mid-evaluation.
Common situations: App update removed/renamed the internal objects the injected script touches; typo or version mismatch in the injected script; evaluating before the page finished loading; awaitPromise rejecting on network-dependent code.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- periodic Runtime.evaluate reported unavailable capability
- browser WebSocket URL has no Browser ID
- browser WebSocket URL has no path
- CDP command id failed
- CDP websocket closed before response for
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/bad60fb191971ad0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/bridge.rs:963
}
}
sent.map(|_| ())
}
}
fn command_result(response: Value, method: &str, message_id: u64) -> anyhow::Result<Value> {
if let Some(error) = response.get("error") {
bail!("CDP command {method} id {message_id} failed: {error}");
}
Ok(response)
}
fn ensure_runtime_evaluate_succeeded(response: Value) -> anyhow::Result<Value> {
if let Some(exception) = response
.get("result")
.and_then(|result| result.get("exceptionDetails"))
{
bail!("Runtime.evaluate raised an exception: {exception}");
}
Ok(response)
}
fn runtime_evaluate_result_is_false(response: &Value) -> bool {
response
.get("result")
.and_then(|result| result.get("result"))
.and_then(|result| result.get("value"))
.is_some_and(|value| value == false)
}
fn extract_string_field(input: &str, field: &str) -> Option<String> {
let needle = format!("\"{field}\"");
let mut index = input.find(&needle)? + needle.len();
let bytes = input.as_bytes();
while matches!(bytes.get(index), Some(b' ' | b'\n' | b'\r' | b'\t')) {View on GitHub (pinned to b1ed92e5e4)