diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 6475531c0..86c2218bc 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -2754,6 +2754,24 @@ impl CodexMessageProcessor { if state_db_ctx.is_none() { state_db_ctx = get_state_db(&self.config).await; } + if state_db_ctx.is_none() { + match StateRuntime::init( + self.config.sqlite_home.clone(), + self.config.model_provider_id.clone(), + ) + .await + { + Ok(ctx) => { + state_db_ctx = Some(ctx); + } + Err(err) => { + warn!( + "failed to initialize state db for thread metadata update at {}: {err}", + self.config.sqlite_home.display() + ); + } + } + } let Some(state_db_ctx) = state_db_ctx else { self.send_internal_error( request_id, diff --git a/codex-rs/app-server/tests/suite/v2/thread_metadata_update.rs b/codex-rs/app-server/tests/suite/v2/thread_metadata_update.rs index 8bf9a8a9a..f5c793f74 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_metadata_update.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_metadata_update.rs @@ -223,6 +223,59 @@ async fn thread_metadata_update_repairs_missing_sqlite_row_for_stored_thread() - Ok(()) } +#[tokio::test] +async fn thread_metadata_update_repairs_stored_thread_before_backfill_completes() -> Result<()> { + let server = create_mock_responses_server_repeating_assistant("Done").await; + let codex_home = TempDir::new()?; + create_config_toml(codex_home.path(), &server.uri())?; + let _state_db = + StateRuntime::init(codex_home.path().to_path_buf(), "mock_provider".into()).await?; + + let preview = "Stored thread preview before backfill"; + let thread_id = create_fake_rollout( + codex_home.path(), + "2025-01-05T12-30-00", + "2025-01-05T12:30:00Z", + preview, + Some("mock_provider"), + /*git_info*/ None, + )?; + + let mut mcp = McpProcess::new(codex_home.path()).await?; + timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; + + let update_id = mcp + .send_thread_metadata_update_request(ThreadMetadataUpdateParams { + thread_id: thread_id.clone(), + git_info: Some(ThreadMetadataGitInfoUpdateParams { + sha: None, + branch: Some(Some("feature/pending-backfill".to_string())), + origin_url: None, + }), + }) + .await?; + let update_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(update_id)), + ) + .await??; + let ThreadMetadataUpdateResponse { thread: updated } = + to_response::(update_resp)?; + + assert_eq!(updated.id, thread_id); + assert_eq!(updated.preview, preview); + assert_eq!( + updated.git_info, + Some(GitInfo { + sha: None, + branch: Some("feature/pending-backfill".to_string()), + origin_url: None, + }) + ); + + Ok(()) +} + #[tokio::test] async fn thread_metadata_update_repairs_loaded_thread_without_resetting_summary() -> Result<()> { let server = create_mock_responses_server_repeating_assistant("Done").await;