{"record":{"id":"f741815aff5c5558","repo":"BigPizzaV3/CodexPlusPlus","slug":"provider-sync-requires-launcher-hooks-with-codex-p","errorCode":null,"errorMessage":"provider sync requires launcher hooks with codex-plus-data integration","messagePattern":"provider sync 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":565,"sourceCode":"            Some(settings.codex_app_path.as_str()),\n        )\n        .ok_or_else(|| anyhow::anyhow!(\"Codex App directory not found\"))\n    }\n\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();","sourceCodeStart":547,"sourceCodeEnd":583,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/1f431ae49b57b3055e0e6845ba6156c6b4232b4d/crates/codex-plus-core/src/launcher.rs#L547-L583","documentation":"codex-plus-core's default LaunchHooks implementation is a stub: DefaultLaunchHooks::run_provider_sync (crates/codex-plus-core/src/launcher.rs:564) unconditionally bails with this message. The real implementation lives one layer up in apps/codex-plus-launcher/src/main.rs:328, where LauncherHooks::run_provider_sync delegates to codex_plus_data::run_provider_sync to merge ~/.codex provider auth into the manager. Seeing this error means the launcher was constructed with DefaultLaunchHooks::shared() instead of a data-integrated hooks object, so the operation is genuinely unavailable, not failed.","triggerScenarios":"Calling CodexLauncher::launch (or any flow that invokes LaunchHooks::run_provider_sync) while the launcher was built with DefaultLaunchHooks — typically in unit/integration tests inside codex-plus-core, in a custom binary that embeds the core crate directly, or in the Tauri manager app if it forgets to install its LauncherHooks wrapper.","commonSituations":"Writing new tests against launcher flows using the default hooks; building a downstream binary that depends on codex-plus-core only (no codex-plus-data feature); refactoring that accidentally swaps LauncherHooks back to DefaultLaunchHooks::shared(); version upgrades that rename the hooks trait so the override impl silently stops applying (no compile error if trait method signature changed and the impl was dropped).","solutions":["Use the launcher binary in apps/codex-plus-launcher (LauncherHooks implements run_provider_sync via codex_plus_data::run_provider_sync) instead of a core-only embedding","If you embed codex-plus-core in your own binary, implement LaunchHooks yourself and call codex_plus_data::run_provider_sync(None) inside run_provider_sync, mirroring apps/codex-plus-launcher/src/main.rs:328","If the operation is genuinely optional in your context, treat this error as non-fatal: log it and continue (the launcher app ignores sync failures rather than aborting launch)","In tests, inject a mock LaunchHooks whose run_provider_sync returns Ok(()) so the stub bail never fires"],"exampleFix":"// before (core-only wiring hits the stub)\nlet hooks = DefaultLaunchHooks::shared();\nlet launcher = CodexLauncher::with_hooks(hooks);\nlauncher.launch(...).await?; // run_provider_sync bails\n\n// after (data-backed hooks)\nstruct DataBackedHooks;\n#[async_trait(?Send)]\nimpl LaunchHooks for DataBackedHooks {\n    async fn run_provider_sync(&self) -> anyhow::Result<()> {\n        tokio::task::spawn_blocking(|| codex_plus_data::run_provider_sync(None))\n            .await\n            .map_err(|e| anyhow::anyhow!(\"provider sync task failed: {e}\"))??;\n        Ok(())\n    }\n    // ...delegate remaining methods to DefaultLaunchHooks\n}\nlet launcher = CodexLauncher::with_hooks(Arc::new(DataBackedHooks));","handlingStrategy":"fallback","validationCode":"// Before launching, verify the hooks you wired are data-integrated\nfn hooks_support_provider_sync(hooks: &dyn LaunchHooks) -> bool {\n    // probe cheaply: default stub always fails; real impl succeeds/idempotently no-ops\n    // simplest: assert wiring at construction time in your binary\n    true\n}\n// Prefer compile-time wiring instead of runtime probing:\n// construct the launcher only via your DataBackedHooks type.","typeGuard":"// Rust has no runtime trait-downcast guard needed if you own the type;\n// narrow via as_any when mixing hook sources:\nimpl LaunchHooks for DataBackedHooks {\n    fn as_any(&self) -> &dyn std::any::Any { self }\n}\nfn is_data_backed(hooks: &dyn LaunchHooks) -> bool {\n    hooks.as_any().downcast_ref::<DataBackedHooks>().is_some()\n}","tryCatchPattern":"match hooks.run_provider_sync().await {\n    Ok(()) => {}\n    Err(e) if e.to_string().contains(\"requires launcher hooks with codex-plus-data integration\") => {\n        tracing::warn!(\"provider sync unavailable in this embedding; skipping\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always construct launchers through the apps/codex-plus-launcher wiring or your own LaunchHooks impl that calls codex_plus_data","Add a construction-time assertion (or unit test) that the installed hooks are not DefaultLaunchHooks when provider sync matters","Keep the codex-plus-data dependency in the same workspace so the hook impl cannot be silently dropped"],"tags":["rust","launcher","dependency-injection","stub-implementation","provider-sync"],"backgroundTag":"operation-not-implemented","analyzedSha":"1f431ae49b57b3055e0e6845ba6156c6b4232b4d","analyzedAt":"2026-08-16T20:54:18.598Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}