{"record":{"id":"f98832c4d80b45de","repo":"openai/codex","slug":"filename-filename-reason","errorCode":null,"errorMessage":"filename '{filename}' {reason}","messagePattern":"filename '(.+?)' (.+?)","errorType":"validation","errorClass":"MemoriesBackendError","httpStatus":null,"severity":"error","filePath":"codex-rs/ext/memories/src/backend.rs","lineNumber":137,"sourceCode":"#[serde(rename_all = \"snake_case\")]\npub enum MemoryEntryType {\n    File,\n    Directory,\n}\n\n#[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\")]","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/ext/memories/src/backend.rs#L119-L155","documentation":"MemoriesBackend::add_ad_hoc_note validates the requested filename before storing; the reason string completes the sentence ('filename X <reason>') and names the exact violation. Backends keep paths relative to the memory store, so filenames must be plain single-component names.","triggerScenarios":"add_ad_hoc_note with a filename containing path separators, '..' traversal, invalid characters, or otherwise violating the backend's storage rules (the reason says which).","commonSituations":"Letting model or user input become the filename verbatim; porting note names from another OS (backslashes, colons); hidden or relative names.","solutions":["Read the reason field — it states the precise rule broken.","Use a plain single-component filename (letters, digits, dash, underscore, extension).","Sanitize or normalize filenames before calling add_ad_hoc_note.","Generate slug names from titles instead of accepting raw input."],"exampleFix":"// before\nbackend.add_ad_hoc_note(AddAdHocMemoryNoteRequest {\n    filename: \"../notes/My Note.md\".into(),\n    note,\n}).await?;\n\n// after\nbackend.add_ad_hoc_note(AddAdHocMemoryNoteRequest {\n    filename: \"my-note.md\".into(),\n    note,\n}).await?;","handlingStrategy":"validation","validationCode":"let filename = sanitize(&raw_title); // slug: lowercase, [-a-z0-9_.]\nif !is_valid_memory_filename(&filename) {\n    return Err(MemoriesBackendError::invalid_filename(filename, \"contains path separators\"));\n}","typeGuard":"fn is_valid_memory_filename(name: &str) -> bool {\n    !name.is_empty()\n        && !name.starts_with('.')\n        && !name.contains(['/', '\\\\', ':'])\n        && !name.contains(\"..\")\n        && name == name.trim()\n}","tryCatchPattern":null,"preventionTips":["Never pass user or model text as a filename verbatim — slug it first.","Read the reason field to learn the exact violated rule.","Keep filenames single-component and store-relative."],"tags":["rust","memories","validation","filename","codex"],"backgroundTag":"invalid-filename","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}