openai/codex · error · MemoriesBackendError

path '{path}' is not a file

Error message

path '{path}' is not a file

What it means

Returned by the memories read path (codex-rs/ext/memories/src/local/read.rs:30-32) when the path resolves and metadata exists but the entry is not a regular file, almost always a directory. read() only returns file content; symlinks are rejected earlier by a dedicated check. The list() API marks every entry with MemoryEntryType::File or MemoryEntryType::Directory exactly so callers can avoid this.

Source

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

    #[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),
}

impl MemoriesBackendError {
    pub fn invalid_filename(filename: impl Into<String>, reason: impl Into<String>) -> Self {
        Self::InvalidFilename {
            filename: filename.into(),
            reason: reason.into(),
        }
    }

    pub fn invalid_path(path: impl Into<String>, reason: impl Into<String>) -> Self {

View on GitHub (pinned to 339751715c)

Solutions

  1. Call list() first and only read entries whose entry_type is MemoryEntryType::File.
  2. To inspect a directory, list() it with the directory as path.
  3. To locate content across files, use search(), which walks directories itself.

Example fix

// before
for entry in backend.list(list_req).await?.entries {
    let resp = backend.read(read_req(entry.path)).await?; // NotFile on directories
}

// after
for entry in backend.list(list_req).await?.entries {
    if is_memory_file(&entry) {
        let resp = backend.read(read_req(entry.path)).await?;
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Rust - filter a listing down to readable files before calling read
let entries = backend.list(ListMemoriesRequest { path: Some(dir), cursor: None, max_results: 100 }).await?;
let readable: Vec<_> = entries.entries.into_iter().filter(is_memory_file).collect();

Type guard

// Rust - narrow MemoryEntry to entries read() accepts
fn is_memory_file(entry: &MemoryEntry) -> bool {
    matches!(entry.entry_type, MemoryEntryType::File)
}

Prevention

When it happens

Trigger: Calling read() on a path that list() reported as MemoryEntryType::Directory; passing the memory root or a subfolder; reading a special file (fifo, device) that exists in the store.

Common situations: An agent or automation enumerating the store and reading every entry without checking entry_type; assuming list() results are all files; joining a folder with an empty filename.

Related errors


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