mirror of
https://github.com/openai/codex.git
synced 2026-11-12 21:20:34 +00:00
Distinguish thread listing failures from exhausted history (#51602)
## Why DB-only listing and pagination failures must not look like exhausted history. This hardens the DB-only listing added in https://github.com/openai/codex/pull/18502 and page refilling added in https://github.com/openai/codex/pull/7509. ## What changed - Return JSON-RPC error `-32603` when `useStateDbOnly: true` cannot query the local state database, including requests with an empty `cwd` array. - Track all cursors encountered while filling a page and return `-32603` for repeated cursors, including on full pages. - Document that callers can retry these errors; a successful response with `nextCursor: null` still indicates exhaustion. Default scan-and-repair requests retain their filesystem fallback. ## Testing Add regression tests for missing or mismatched state databases versus healthy empty history, with active and archived listings and null or empty `cwd` filters. Cover repeated cursors during page refilling and on full pages using the in-memory thread store. GitOrigin-RevId: 09653a84fc65f47316d8bbae9e4cf3dd8ce9948a
This commit is contained in:
@@ -5503,7 +5503,7 @@ impl ThreadRequestProcessor {
|
||||
excluded_thread_ids,
|
||||
} = filters;
|
||||
let mut cursor_obj = cursor;
|
||||
let mut last_cursor = cursor_obj.clone();
|
||||
let mut seen_cursors: HashSet<String> = cursor_obj.iter().cloned().collect();
|
||||
let mut remaining = requested_page_size;
|
||||
let mut items = Vec::with_capacity(requested_page_size);
|
||||
let mut next_cursor: Option<String> = None;
|
||||
@@ -5580,20 +5580,16 @@ impl ThreadRequestProcessor {
|
||||
remaining = requested_page_size.saturating_sub(items.len());
|
||||
|
||||
next_cursor = page.next_cursor;
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let Some(cursor_val) = next_cursor.clone() else {
|
||||
break;
|
||||
};
|
||||
// Break if our pagination would reuse the same cursor again; this avoids
|
||||
// an infinite loop when filtering drops everything on the page.
|
||||
if last_cursor.as_ref() == Some(&cursor_val) {
|
||||
next_cursor = None;
|
||||
// Check full pages too: returning a repeated cursor would loop on the next request.
|
||||
if !seen_cursors.insert(cursor_val.clone()) {
|
||||
return Err(internal_error("thread listing returned a repeated cursor"));
|
||||
}
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
last_cursor = Some(cursor_val.clone());
|
||||
cursor_obj = Some(cursor_val);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user