{"record":{"id":"42d461a0f1772a54","repo":"astrid-runtime/astrid","slug":"private-path-has-no-file-name","errorCode":null,"errorMessage":"private path has no file name: {}","messagePattern":"private path has no file name: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/platform_fs.rs","lineNumber":438,"sourceCode":"fn verify_no_redirects_unix(path: &Path) -> io::Result<()> {\n    use nix::fcntl::{OFlag, openat};\n    use nix::sys::stat::Mode;\n\n    match std::fs::symlink_metadata(path) {\n        Ok(metadata) if metadata.file_type().is_symlink() => Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"private path is redirected: {}\", path.display()),\n        )),\n        Ok(metadata) if metadata.is_dir() => open_directory_no_follow_unix(path).map(drop),\n        Ok(_) => {\n            let parent = path.parent().ok_or_else(|| {\n                io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    format!(\"private path has no parent: {}\", path.display()),\n                )\n            })?;\n            let name = path.file_name().ok_or_else(|| {\n                io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    format!(\"private path has no file name: {}\", path.display()),\n                )\n            })?;\n            let directory = open_directory_no_follow_unix(parent)?;\n            let flags = OFlag::O_RDONLY | OFlag::O_NOFOLLOW | OFlag::O_CLOEXEC | OFlag::O_NONBLOCK;\n            openat(&directory, name, flags, Mode::empty())\n                .map(std::fs::File::from)\n                .map(drop)\n                .map_err(nix_io_error)\n        },\n        Err(error) if error.kind() == io::ErrorKind::NotFound => {\n            open_directory_no_follow_unix(path).map(drop)\n        },\n        Err(error) => Err(error),\n    }\n}\n","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/platform_fs.rs#L420-L456","documentation":"After resolving the parent, verify_no_redirects_unix extracts the final component with path.file_name() to open it with O_NOFOLLOW. Paths ending in \"..\", the root, or otherwise lacking a final component return None, producing io::ErrorKind::InvalidInput.","triggerScenarios":"Calling verify_no_redirects with a path ending in \"..\" (e.g. /home/user/.astrid/..) or another path with no resolvable file-name component.","commonSituations":"Paths built by string concatenation with \"..\" segments; user-supplied config paths not normalized; code that joins a directory with \"..\" expecting it to resolve.","solutions":["Normalize the path (drop or resolve \"..\" components, e.g. via lexical normalization or canonicalize the parent) before calling.","Pass a path whose final component is a real entry name.","Validate user-supplied paths to reject trailing \"..\" components."],"exampleFix":"// before\nlet p = Path::new(\"/home/user/.astrid/..\");\nverify_no_redirects(p)?;\n// after\nlet p = Path::new(\"/home/user/.astrid/..\").canonicalize()?; // -> /home/user\nverify_no_redirects(&p)?;","handlingStrategy":"validation","validationCode":"if path.file_name().is_none() {\n    return Err(format!(\"path {} has no final component\", path.display()));\n}","typeGuard":"fn has_file_name(p: &std::path::Path) -> bool { p.file_name().is_some() }","tryCatchPattern":"match verify_no_redirects(&path) {\n    Err(e) if e.kind() == io::ErrorKind::InvalidInput => eprintln!(\"normalize the path (trailing '..' or root): {e}\"),\n    other => other?,\n}","preventionTips":["Canonicalize or lexically normalize paths before validation","Reject user input containing \"..\" components","Build paths via PathBuf::join from an anchor rather than string concatenation"],"tags":["io","filesystem","invalid-path"],"backgroundTag":"invalid-argument-format","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}