diff --git a/codex-rs/core/src/rollout/list.rs b/codex-rs/core/src/rollout/list.rs index 34e144e5689..2cac898d42e 100644 --- a/codex-rs/core/src/rollout/list.rs +++ b/codex-rs/core/src/rollout/list.rs @@ -1091,17 +1091,22 @@ async fn find_thread_path_by_id_str_in_subdir( let state_db_ctx = state_db::open_if_present(codex_home, "").await; if let Some(state_db_ctx) = state_db_ctx.as_deref() && let Ok(thread_id) = ThreadId::from_string(id_str) - { - let db_path = state_db::find_rollout_path_by_id( + && let Some(db_path) = state_db::find_rollout_path_by_id( Some(state_db_ctx), thread_id, archived_only, "find_path_query", ) - .await; - if db_path.is_some() { - return Ok(db_path); + .await + { + if tokio::fs::try_exists(&db_path).await.unwrap_or(false) { + return Ok(Some(db_path)); } + tracing::error!( + "state db returned stale rollout path for thread {id_str}: {}", + db_path.display() + ); + state_db::record_discrepancy("find_thread_path_by_id_str_in_subdir", "stale_db_path"); } let mut root = codex_home.to_path_buf(); diff --git a/codex-rs/core/src/rollout/tests.rs b/codex-rs/core/src/rollout/tests.rs index e42e1861576..89df2897dba 100644 --- a/codex-rs/core/src/rollout/tests.rs +++ b/codex-rs/core/src/rollout/tests.rs @@ -232,6 +232,27 @@ async fn list_threads_falls_back_to_files_when_state_db_is_unavailable() { ); } +#[tokio::test] +async fn find_thread_path_falls_back_when_db_path_is_stale() { + let temp = TempDir::new().unwrap(); + let home = temp.path(); + let uuid = Uuid::from_u128(302); + let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id"); + let ts = "2025-01-03T13-00-00"; + write_session_file(home, ts, uuid, 1, Some(SessionSource::Cli)).unwrap(); + let fs_rollout_path = home.join(format!("sessions/2025/01/03/rollout-{ts}-{uuid}.jsonl")); + + let stale_db_path = home.join(format!( + "sessions/2099/01/01/rollout-2099-01-01T00-00-00-{uuid}.jsonl" + )); + insert_state_db_thread(home, thread_id, stale_db_path.as_path(), false).await; + + let found = crate::rollout::find_thread_path_by_id_str(home, &uuid.to_string()) + .await + .expect("lookup should succeed"); + assert_eq!(found, Some(fs_rollout_path)); +} + #[test] fn rollout_date_parts_extracts_directory_components() { let file_name = OsStr::new("rollout-2025-03-01T09-00-00-123.jsonl"); diff --git a/codex-rs/core/tests/suite/rollout_list_find.rs b/codex-rs/core/tests/suite/rollout_list_find.rs index 8786c9be21a..5b545384b97 100644 --- a/codex-rs/core/tests/suite/rollout_list_find.rs +++ b/codex-rs/core/tests/suite/rollout_list_find.rs @@ -104,9 +104,11 @@ async fn find_prefers_sqlite_path_by_id() { let home = TempDir::new().unwrap(); let id = Uuid::new_v4(); let thread_id = ThreadId::from_string(&id.to_string()).unwrap(); - let db_path = home - .path() - .join("sessions/2030/12/30/rollout-2030-12-30T00-00-00-db.jsonl"); + let db_path = home.path().join(format!( + "sessions/2030/12/30/rollout-2030-12-30T00-00-00-{id}.jsonl" + )); + std::fs::create_dir_all(db_path.parent().unwrap()).unwrap(); + std::fs::write(&db_path, "").unwrap(); write_minimal_rollout_with_id(home.path(), id); upsert_thread_metadata(home.path(), thread_id, db_path.clone()).await;