openai/codex · error · MemoriesBackendError

cursor '{cursor}' {reason}

Error message

cursor '{cursor}' {reason}

What it means

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.

Source

Thrown at codex-rs/ext/memories/src/backend.rs:145

pub struct MemorySearchMatch {
    pub path: String,
    pub match_line_number: usize,
    pub content_start_line_number: usize,
    pub content: String,
    pub matched_queries: Vec<String>,
}

#[derive(Debug, thiserror::Error)]
pub enum MemoriesBackendError {
    #[error("filename '{filename}' {reason}")]
    InvalidFilename { filename: String, reason: String },
    #[error("ad-hoc note must not be empty")]
    EmptyAdHocNote,
    #[error("ad-hoc note '{filename}' already exists")]
    AdHocNoteAlreadyExists { filename: String },
    #[error("path '{path}' {reason}")]
    InvalidPath { path: String, reason: String },
    #[error("cursor '{cursor}' {reason}")]
    InvalidCursor { cursor: String, reason: String },
    #[error("path '{path}' was not found")]
    NotFound { path: String },
    #[error("line_offset must be a 1-indexed line number")]
    InvalidLineOffset,
    #[error("max_lines must be a positive integer")]
    InvalidMaxLines,
    #[error("line_offset exceeds file length")]
    LineOffsetExceedsFileLength,
    #[error("path '{path}' is not a file")]
    NotFile { path: String },
    #[error("queries must not be empty or contain empty strings")]
    EmptyQuery,
    #[error("all_within_lines.line_count must be a positive integer")]
    InvalidMatchWindow,
    #[error("I/O error while reading memories: {0}")]
    Io(#[from] std::io::Error),
}

View on GitHub (pinned to 339751715c)

Solutions

  1. Only pass the next_cursor returned by the immediately preceding call of the same query.
  2. On InvalidCursor, restart pagination from the first page (cursor None).
  3. Treat cursor strings as opaque — never construct or mutate them.
Defensive patterns

Strategy: validation

Validate before calling

// Only ever forward the opaque token from the previous same-query response
let cursor = previous.next_cursor.clone();
// On any pagination restart, send cursor: None instead of a remembered token

Try / catch

match backend.list(req).await {
    Err(MemoriesBackendError::InvalidCursor { cursor, reason }) => {
        tracing::warn!(%cursor, %reason, "stale cursor; restarting from first page");
        backend.list(req_with_first_page).await
    }
    r => r,
}

Prevention

When it happens

Trigger: Reusing a next_cursor from an earlier or different query, editing or truncating a cursor string, or continuing pagination after the store changed underneath.

Common situations: Resuming a listing from stale saved state; mixing cursors between list and search; concurrent modifications invalidating tokens.

Related errors


AI-assisted analysis of openai/codex@339751715c (2026-08-25). Data as JSON: /api/errors/679ce417d950c72b. Report an issue: GitHub.