mirror of
https://github.com/openai/codex.git
synced 2026-11-13 22:47:20 +00:00
Fix Windows running thread resume path normalization (#25509)
## Why Fixes #24944. On Windows, app-server resume could reject an active running thread when the requested session path used normal `C:\...` form and the already-running path used verbatim `\\?\C:\...` form. The paths point at the same JSONL file, but the resume stale-path guard compared raw `PathBuf`s, so desktop resume and heartbeat flows could fail with a mismatched-path error. ## What Changed - Compare requested and active rollout paths with `path_utils::paths_match_after_normalization`. - Extend the existing running-thread mismatched-path test with a Windows-only same-file resume case before the stale-path rejection. ## Verification - `just test -p codex-app-server thread_resume_rejects_mismatched_path_for_running_thread_id`
This commit is contained in:
1 parent
af18e92140
commit
ad355d4c96
2 files changed
+31
-1
No files matched your search
@@ -2766,7 +2766,7 @@ impl ThreadRequestProcessor {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.or(source_thread.rollout_path.as_ref());
|
.or(source_thread.rollout_path.as_ref());
|
||||||
if let (Some(requested_path), Some(active_path)) = (params.path.as_ref(), active_path)
|
if let (Some(requested_path), Some(active_path)) = (params.path.as_ref(), active_path)
|
||||||
&& requested_path != active_path
|
&& !path_utils::paths_match_after_normalization(requested_path, active_path)
|
||||||
{
|
{
|
||||||
return Err(invalid_request(format!(
|
return Err(invalid_request(format!(
|
||||||
"cannot resume running thread {existing_thread_id} with stale path: requested `{}`, active `{}`",
|
"cannot resume running thread {existing_thread_id} with stale path: requested `{}`, active `{}`",
|
||||||
|
|||||||
@@ -2410,6 +2410,36 @@ async fn thread_resume_rejects_mismatched_path_for_running_thread_id() -> Result
|
|||||||
)
|
)
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let active_path = thread.path.as_ref().expect("thread should have path");
|
||||||
|
let active_path_display = active_path.as_os_str().to_string_lossy();
|
||||||
|
let equivalent_path = if let Some(path) = active_path_display.strip_prefix(r"\\?\UNC\") {
|
||||||
|
PathBuf::from(format!(r"\\{path}"))
|
||||||
|
} else if let Some(path) = active_path_display.strip_prefix(r"\\?\") {
|
||||||
|
PathBuf::from(path)
|
||||||
|
} else if let Some(path) = active_path_display.strip_prefix(r"\\") {
|
||||||
|
PathBuf::from(format!(r"\\?\UNC\{path}"))
|
||||||
|
} else {
|
||||||
|
PathBuf::from(format!(r"\\?\{active_path_display}"))
|
||||||
|
};
|
||||||
|
let normalized_resume_id = primary
|
||||||
|
.send_thread_resume_request(ThreadResumeParams {
|
||||||
|
thread_id: thread_id.clone(),
|
||||||
|
path: Some(equivalent_path),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
let normalized_resume_resp: JSONRPCResponse = timeout(
|
||||||
|
DEFAULT_READ_TIMEOUT,
|
||||||
|
primary.read_stream_until_response_message(RequestId::Integer(normalized_resume_id)),
|
||||||
|
)
|
||||||
|
.await??;
|
||||||
|
let ThreadResumeResponse { thread, .. } =
|
||||||
|
to_response::<ThreadResumeResponse>(normalized_resume_resp)?;
|
||||||
|
assert_eq!(thread.id, thread_id);
|
||||||
|
}
|
||||||
|
|
||||||
let stale_thread_id = Uuid::new_v4().to_string();
|
let stale_thread_id = Uuid::new_v4().to_string();
|
||||||
let stale_path = rollout_path(codex_home.path(), "2025-01-01T00-00-00", &stale_thread_id);
|
let stale_path = rollout_path(codex_home.path(), "2025-01-01T00-00-00", &stale_thread_id);
|
||||||
std::fs::create_dir_all(stale_path.parent().expect("stale path parent"))?;
|
std::fs::create_dir_all(stale_path.parent().expect("stale path parent"))?;
|
||||||
|
|||||||
Reference in new issue
Block a user