{"record":{"id":"bad9773c00fd8c74","repo":"BigPizzaV3/CodexPlusPlus","slug":"remote-control-session-recovery-requires-launcher","errorCode":null,"errorMessage":"Remote Control session recovery requires launcher hooks with codex-plus-data integration","messagePattern":"Remote Control session recovery requires launcher hooks with codex-plus-data integration","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/launcher.rs","lineNumber":569,"sourceCode":"\n    fn select_debug_port(&self, requested: u16) -> u16 {\n        crate::ports::select_packaged_codex_debug_port(requested)\n    }\n\n    fn select_helper_port(&self, requested: u16) -> u16 {\n        crate::ports::select_platform_loopback_port(requested)\n    }\n\n    async fn load_settings(&self) -> anyhow::Result<BackendSettings> {\n        SettingsStore::default().load()\n    }\n\n    async fn run_provider_sync(&self) -> anyhow::Result<()> {\n        anyhow::bail!(\"provider sync requires launcher hooks with codex-plus-data integration\")\n    }\n\n    async fn run_remote_control_session_recovery(&self) -> anyhow::Result<()> {\n        anyhow::bail!(\n            \"Remote Control session recovery requires launcher hooks with codex-plus-data integration\"\n        )\n    }\n\n    fn remote_control_session_recovery_is_safe_to_run(&self) -> bool {\n        crate::watcher::find_session_index_cleanup_blocking_processes().is_empty()\n    }\n\n    async fn apply_active_relay_profile(&self, settings: &BackendSettings) -> anyhow::Result<()> {\n        if !settings.relay_profiles_enabled {\n            return Ok(());\n        }\n        let profile = settings.active_relay_profile();\n        let home = crate::relay_config::default_codex_home_dir();\n        let common_config = crate::relay_config::normalize_config_text(\n            &[\n                settings.relay_common_config_contents.as_str(),\n                settings.relay_context_config_contents.as_str(),","sourceCodeStart":551,"sourceCodeEnd":587,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/1f431ae49b57b3055e0e6845ba6156c6b4232b4d/crates/codex-plus-core/src/launcher.rs#L551-L587","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run through apps/codex-plus-launcher, whose LauncherHooks implements run_remote_control_session_recovery with codex_plus_data","Implement LaunchHooks::run_remote_control_session_recovery in your own embedding and delegate to codex_plus_data's recovery routine","If recovery is not needed, clear the pending markers (delete the pending remote-control recovery file) so the launcher never attempts it","For tests, stub the hook to return Ok(())"],"exampleFix":"// before\nasync fn run_remote_control_session_recovery(&self) -> anyhow::Result<()> {\n    anyhow::bail!(\"Remote Control session recovery requires launcher hooks with codex-plus-data integration\")\n}\n\n// after (in your LaunchHooks impl)\nasync fn run_remote_control_session_recovery(&self) -> anyhow::Result<()> {\n    codex_plus_data::run_remote_control_session_recovery().await\n}","handlingStrategy":"fallback","validationCode":"// Skip recovery attempt entirely when no markers are pending\nuse codex_plus_core::paths::default_pending_remote_control_recovery_path;\nif !default_pending_remote_control_recovery_path().exists() {\n    // launcher will not attempt recovery; stub error cannot fire\n}","typeGuard":"fn recovery_unavailable_error(e: &anyhow::Error) -> bool {\n    e.to_string().contains(\"Remote Control session recovery requires launcher hooks\")\n}","tryCatchPattern":"if let Err(e) = hooks.run_remote_control_session_recovery().await {\n    if e.to_string().contains(\"launcher hooks with codex-plus-data integration\") {\n        tracing::warn!(\"session recovery unavailable; continuing without it\");\n    } else {\n        return Err(e);\n    }\n}","preventionTips":["Wire LauncherHooks from codex-plus-launcher (or your own impl) whenever Remote Control sessions are used","Clear stale pending-recovery markers when tearing down test environments","Gate recovery behind remote_control_session_recovery_is_safe_to_run() plus a hooks-capability check"],"tags":["rust","launcher","remote-control","session-recovery","stub-implementation"],"backgroundTag":"operation-not-implemented","analyzedSha":"1f431ae49b57b3055e0e6845ba6156c6b4232b4d","analyzedAt":"2026-08-16T20:54:18.598Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}