{"record":{"id":"679ce417d950c72b","repo":"openai/codex","slug":"cursor-cursor-reason","errorCode":null,"errorMessage":"cursor '{cursor}' {reason}","messagePattern":"cursor '(.+?)' (.+?)","errorType":"validation","errorClass":"MemoriesBackendError","httpStatus":null,"severity":"error","filePath":"codex-rs/ext/memories/src/backend.rs","lineNumber":145,"sourceCode":"pub struct MemorySearchMatch {\n    pub path: String,\n    pub match_line_number: usize,\n    pub content_start_line_number: usize,\n    pub content: String,\n    pub matched_queries: Vec<String>,\n}\n\n#[derive(Debug, thiserror::Error)]\npub enum MemoriesBackendError {\n    #[error(\"filename '{filename}' {reason}\")]\n    InvalidFilename { filename: String, reason: String },\n    #[error(\"ad-hoc note must not be empty\")]\n    EmptyAdHocNote,\n    #[error(\"ad-hoc note '{filename}' already exists\")]\n    AdHocNoteAlreadyExists { filename: String },\n    #[error(\"path '{path}' {reason}\")]\n    InvalidPath { path: String, reason: String },\n    #[error(\"cursor '{cursor}' {reason}\")]\n    InvalidCursor { cursor: String, reason: String },\n    #[error(\"path '{path}' was not found\")]\n    NotFound { path: String },\n    #[error(\"line_offset must be a 1-indexed line number\")]\n    InvalidLineOffset,\n    #[error(\"max_lines must be a positive integer\")]\n    InvalidMaxLines,\n    #[error(\"line_offset exceeds file length\")]\n    LineOffsetExceedsFileLength,\n    #[error(\"path '{path}' is not a file\")]\n    NotFile { path: String },\n    #[error(\"queries must not be empty or contain empty strings\")]\n    EmptyQuery,\n    #[error(\"all_within_lines.line_count must be a positive integer\")]\n    InvalidMatchWindow,\n    #[error(\"I/O error while reading memories: {0}\")]\n    Io(#[from] std::io::Error),\n}","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/ext/memories/src/backend.rs#L127-L163","documentation":"A pagination cursor passed to list or search failed validation — the reason says whether it was malformed, unknown, or belongs to a different listing. Cursors are opaque tokens minted by the backend, not free-form offsets.","triggerScenarios":"Reusing a next_cursor from an earlier or different query, editing or truncating a cursor string, or continuing pagination after the store changed underneath.","commonSituations":"Resuming a listing from stale saved state; mixing cursors between list and search; concurrent modifications invalidating tokens.","solutions":["Only pass the next_cursor returned by the immediately preceding call of the same query.","On InvalidCursor, restart pagination from the first page (cursor None).","Treat cursor strings as opaque — never construct or mutate them."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Only ever forward the opaque token from the previous same-query response\nlet cursor = previous.next_cursor.clone();\n// On any pagination restart, send cursor: None instead of a remembered token","typeGuard":null,"tryCatchPattern":"match backend.list(req).await {\n    Err(MemoriesBackendError::InvalidCursor { cursor, reason }) => {\n        tracing::warn!(%cursor, %reason, \"stale cursor; restarting from first page\");\n        backend.list(req_with_first_page).await\n    }\n    r => r,\n}","preventionTips":["Treat cursors as opaque — never build or edit them.","Scope a cursor to the exact query that produced it; do not reuse across list/search.","Restart from page one on cursor invalidation instead of failing the whole listing."],"tags":["rust","memories","pagination","cursor","codex"],"backgroundTag":"invalid-pagination-cursor","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}