{"record":{"id":"4b364fa07235a373","repo":"openai/codex","slug":"path-path-reason","errorCode":null,"errorMessage":"path '{path}' {reason}","messagePattern":"path '(.+?)' (.+?)","errorType":"validation","errorClass":"MemoriesBackendError","httpStatus":null,"severity":"error","filePath":"codex-rs/ext/memories/src/backend.rs","lineNumber":143,"sourceCode":"#[derive(Debug, Clone, PartialEq, Eq, Serialize, JsonSchema)]\n#[schemars(deny_unknown_fields)]\npub 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}\")]","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/ext/memories/src/backend.rs#L125-L161","documentation":"The path argument to list/read/search failed validation; the reason string names the exact problem. The MemoriesBackend contract keeps paths relative to the memory store, so absolute paths and traversal are rejected by design.","triggerScenarios":"Passing a path starting with '/' or a drive letter, containing '..' components, empty segments, or otherwise violating the backend's relative-path rules.","commonSituations":"Feeding absolute host filesystem paths into memory tools; concatenated input producing 'a/../b'; Windows-style separators.","solutions":["Pass store-relative paths ('notes/foo.md', not '/home/.../notes/foo.md').","Normalize: strip leading slashes and reject '..' segments before calling.","Consult the reason text for the specific rule that was tripped."],"exampleFix":"// before\nlet req = ReadMemoryRequest { path: \"/home/user/store/notes/a.md\".into(), .. };\n\n// after\nlet req = ReadMemoryRequest { path: \"notes/a.md\".into(), .. };","handlingStrategy":"validation","validationCode":"let path = normalize_store_path(&raw_path)?; // strip leading '/', collapse '//'\nif !is_valid_store_path(&path) {\n    return Err(MemoriesBackendError::invalid_path(path, \"absolute or traversing paths are not allowed\"));\n}","typeGuard":"fn is_valid_store_path(path: &str) -> bool {\n    !path.is_empty()\n        && !path.starts_with('/')\n        && !path.split('/').any(|seg| seg.is_empty() || seg == \"..\" || seg == \".\")\n}","tryCatchPattern":null,"preventionTips":["Always pass store-relative paths, never absolute host paths.","Reject '..' segments at the API boundary.","Read the reason text — it names the exact violated rule."],"tags":["rust","memories","validation","path","codex"],"backgroundTag":"invalid-path","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}