BigPizzaV3/CodexPlusPlus · error · anyhow::Error

Remote Control session recovery requires launcher hooks with

Error message

Remote Control session recovery requires launcher hooks with codex-plus-data integration

What it means

DefaultLaunchHooks::run_remote_control_session_recovery (crates/codex-plus-core/src/launcher.rs:568) is a stub that always bails. Remote Control session recovery restores pending sessions recorded under ~/.codex-plus (see default_pending_remote_control_recovery_path) after a crash or restart, and requires codex-plus-data to enumerate and replay them. Only the launcher app's LauncherHooks (apps/codex-plus-launcher/src/main.rs:307+) implements it; the core default deliberately refuses instead of silently skipping recovery.

Source

Thrown at crates/codex-plus-core/src/launcher.rs:569

    fn select_debug_port(&self, requested: u16) -> u16 {
        crate::ports::select_packaged_codex_debug_port(requested)
    }

    fn select_helper_port(&self, requested: u16) -> u16 {
        crate::ports::select_platform_loopback_port(requested)
    }

    async fn load_settings(&self) -> anyhow::Result<BackendSettings> {
        SettingsStore::default().load()
    }

    async fn run_provider_sync(&self) -> anyhow::Result<()> {
        anyhow::bail!("provider sync requires launcher hooks with codex-plus-data integration")
    }

    async fn run_remote_control_session_recovery(&self) -> anyhow::Result<()> {
        anyhow::bail!(
            "Remote Control session recovery requires launcher hooks with codex-plus-data integration"
        )
    }

    fn remote_control_session_recovery_is_safe_to_run(&self) -> bool {
        crate::watcher::find_session_index_cleanup_blocking_processes().is_empty()
    }

    async fn apply_active_relay_profile(&self, settings: &BackendSettings) -> anyhow::Result<()> {
        if !settings.relay_profiles_enabled {
            return Ok(());
        }
        let profile = settings.active_relay_profile();
        let home = crate::relay_config::default_codex_home_dir();
        let common_config = crate::relay_config::normalize_config_text(
            &[
                settings.relay_common_config_contents.as_str(),
                settings.relay_context_config_contents.as_str(),

View on GitHub (pinned to 1f431ae49b)

Solutions

  1. Run through apps/codex-plus-launcher, whose LauncherHooks implements run_remote_control_session_recovery with codex_plus_data
  2. Implement LaunchHooks::run_remote_control_session_recovery in your own embedding and delegate to codex_plus_data's recovery routine
  3. If recovery is not needed, clear the pending markers (delete the pending remote-control recovery file) so the launcher never attempts it
  4. For tests, stub the hook to return Ok(())

Example fix

// before
async fn run_remote_control_session_recovery(&self) -> anyhow::Result<()> {
    anyhow::bail!("Remote Control session recovery requires launcher hooks with codex-plus-data integration")
}

// after (in your LaunchHooks impl)
async fn run_remote_control_session_recovery(&self) -> anyhow::Result<()> {
    codex_plus_data::run_remote_control_session_recovery().await
}
Defensive patterns

Strategy: fallback

Validate before calling

// Skip recovery attempt entirely when no markers are pending
use codex_plus_core::paths::default_pending_remote_control_recovery_path;
if !default_pending_remote_control_recovery_path().exists() {
    // launcher will not attempt recovery; stub error cannot fire
}

Type guard

fn recovery_unavailable_error(e: &anyhow::Error) -> bool {
    e.to_string().contains("Remote Control session recovery requires launcher hooks")
}

Try / catch

if let Err(e) = hooks.run_remote_control_session_recovery().await {
    if e.to_string().contains("launcher hooks with codex-plus-data integration") {
        tracing::warn!("session recovery unavailable; continuing without it");
    } else {
        return Err(e);
    }
}

Prevention

When it happens

Trigger: Launching Codex through a code path wired to DefaultLaunchHooks while pending Remote Control recovery markers exist (default_pending_remote_control_recovery_path().exists() is true and remote_control_session_recovery_is_safe_to_run() returns true), so the launcher attempts recovery and hits the stub.

Common situations: Running the core crate's test harness or a custom embedding after a previous Remote Control session crashed; migrating an integration from the full launcher app down to core-only; a refactor that drops the LauncherHooks override so the default trait impl takes over.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16). Data as JSON: /api/errors/bad9773c00fd8c74. Report an issue: GitHub.