BigPizzaV3/CodexPlusPlus · error
Codex injection failed
Error message
Codex injection failed
What it means
The launcher retries injecting the Codex helper script into the Codex browser page over CDP in a loop; each failed attempt records the error in last_error and sleeps 500ms. When the loop exits without a successful injection, it returns the last recorded error — or this generic message via unwrap_or_else when last_error is None, i.e. the loop body never recorded any error at all. It is a fallback sentinel indicating injection never got off the ground rather than a specific failure.
Solutions
- Verify the browser is running with the CDP debug port enabled and that the port matches the launcher configuration
- Check launcher logs immediately before this error for earlier concrete per-attempt errors (those are the real cause)
- Increase the retry window/retries if the Codex page loads slowly
- Ensure the loop always assigns last_error on failure so future errors carry a concrete cause instead of this sentinel
Example fix
// before
Err(last_error.unwrap_or_else(|| anyhow::anyhow!("Codex injection failed")))
// after
Err(last_error.unwrap_or_else(|| {
anyhow::anyhow!("Codex injection failed: no attempt completed within the retry window (check CDP debug port and browser state)")
})) Defensive patterns
Strategy: retry
Validate before calling
// 检查 CDP 调试端口可达后再触发注入
let targets = reqwest::get(format!("http://127.0.0.1:{debug_port}/json")).await?;
if !targets.status().is_success() {
anyhow::bail!("CDP debug port {debug_port} not responding; browser not started with remote debugging?");
} Try / catch
match launcher.inject_codex().await {
Ok(()) => {},
Err(e) if e.to_string().contains("Codex injection failed") => {
eprintln!("injection never started; check CDP port and browser state: {e}");
// 手动重启带调试端口的浏览器后重试
}
Err(e) => eprintln!("injection attempt error: {e}"),
} Prevention
- Always launch the browser with the CDP remote debugging port the launcher expects
- Wait for the Codex page to fully load before triggering injection
- Check per-attempt logs — this sentinel error means a concrete earlier error was (or wasn't) recorded
- Keep last_error assignment unconditional in the retry loop so real causes surface
When it happens
Trigger: The injection retry loop in the launcher finished all attempts without success and last_error was None — meaning no injection attempt ever executed or stored an error (e.g. the loop never entered its attempt branch), so unwrap_or_else produced this placeholder message instead of a concrete cause.
Common situations: Browser not launched with the expected CDP debug port, injected page never became available within the retry window, browser closed mid-retry, or a refactor left the loop unable to reach the injection call so last_error stays None.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- selected CDP target has no websocket URL
- archived lookup task failed
- browser WebSocket URL has no Browser ID
- browser WebSocket URL has no path
- built-in Dream Skin theme cannot be deleted
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/a40cbaf2da9d3d01.
Report an issue: GitHub.
Appendix: source
Thrown at apps/codex-plus-launcher/src/main.rs:1026
async fn inject_with_context(
debug_port: u16,
helper_port: u16,
ctx: BridgeContext,
runtime: Arc<LauncherRuntimeService>,
) -> anyhow::Result<()> {
let mut last_error = None;
for _ in 0..20 {
match try_inject_with_context(debug_port, helper_port, ctx.clone(), runtime.clone()).await {
Ok(()) => return Ok(()),
Err(error) => {
last_error = Some(error);
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
}
}
}
Err(last_error.unwrap_or_else(|| anyhow::anyhow!("Codex injection failed")))
}
fn remote_control_recovery_is_superseded_by_openai(
settings: &codex_plus_core::settings::BackendSettings,
request: &codex_plus_core::remote_control_recovery::PendingRemoteControlRecovery,
) -> bool {
settings.active_relay_id == request.profile_id
&& settings.active_relay_session_provider()
== codex_plus_core::settings::RelaySessionProvider::Openai
}
async fn try_inject_with_context(
debug_port: u16,
helper_port: u16,
ctx: BridgeContext,
runtime: Arc<LauncherRuntimeService>,
) -> anyhow::Result<()> {
let targets = codex_plus_core::cdp::list_targets(debug_port).await?;View on GitHub (pinned to b1ed92e5e4)