BigPizzaV3/CodexPlusPlus · error · anyhow::Error
provider sync requires launcher hooks with codex-plus-data i
Error message
provider sync requires launcher hooks with codex-plus-data integration
What it means
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.
Source
Thrown at crates/codex-plus-core/src/launcher.rs:565
Some(settings.codex_app_path.as_str()),
)
.ok_or_else(|| anyhow::anyhow!("Codex App directory not found"))
}
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();View on GitHub (pinned to 1f431ae49b)
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
Example fix
// before (core-only wiring hits the stub)
let hooks = DefaultLaunchHooks::shared();
let launcher = CodexLauncher::with_hooks(hooks);
launcher.launch(...).await?; // run_provider_sync bails
// after (data-backed hooks)
struct DataBackedHooks;
#[async_trait(?Send)]
impl LaunchHooks for DataBackedHooks {
async fn run_provider_sync(&self) -> anyhow::Result<()> {
tokio::task::spawn_blocking(|| codex_plus_data::run_provider_sync(None))
.await
.map_err(|e| anyhow::anyhow!("provider sync task failed: {e}"))??;
Ok(())
}
// ...delegate remaining methods to DefaultLaunchHooks
}
let launcher = CodexLauncher::with_hooks(Arc::new(DataBackedHooks)); Defensive patterns
Strategy: fallback
Validate before calling
// Before launching, verify the hooks you wired are data-integrated
fn hooks_support_provider_sync(hooks: &dyn LaunchHooks) -> bool {
// probe cheaply: default stub always fails; real impl succeeds/idempotently no-ops
// simplest: assert wiring at construction time in your binary
true
}
// Prefer compile-time wiring instead of runtime probing:
// construct the launcher only via your DataBackedHooks type. Type guard
// Rust has no runtime trait-downcast guard needed if you own the type;
// narrow via as_any when mixing hook sources:
impl LaunchHooks for DataBackedHooks {
fn as_any(&self) -> &dyn std::any::Any { self }
}
fn is_data_backed(hooks: &dyn LaunchHooks) -> bool {
hooks.as_any().downcast_ref::<DataBackedHooks>().is_some()
} Try / catch
match hooks.run_provider_sync().await {
Ok(()) => {}
Err(e) if e.to_string().contains("requires launcher hooks with codex-plus-data integration") => {
tracing::warn!("provider sync unavailable in this embedding; skipping");
}
Err(e) => return Err(e),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- Remote Control session recovery requires launcher hooks with
- macOS open command is empty
- Codex command is empty
- 解压后的请求体超过大小限制
- 不支持的 Content-Encoding:{encoding}
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16).
Data as JSON: /api/errors/f741815aff5c5558.
Report an issue: GitHub.