{"record":{"id":"b23372e562dfb432","repo":"sxyazi/yazi","slug":"invalid-filename-attribute","errorCode":null,"errorMessage":"Invalid filename attribute","messagePattern":"Invalid filename attribute","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"yazi-fs/src/casefold/macos.rs","lineNumber":39,"sourceCode":"\t\tnonneg_ok(unsafe {\n\t\t\tlibc::getattrlist(\n\t\t\t\tpath.as_ptr(),\n\t\t\t\t(&raw mut attrs).cast(),\n\t\t\t\tbuf.as_mut_ptr().cast(),\n\t\t\t\tbuf.len(),\n\t\t\t\tlibc::FSOPT_NOFOLLOW as _,\n\t\t\t)\n\t\t})?;\n\n\t\t// The name offset is relative to the attrreference following the length word.\n\t\tlet len = u32::from_ne_bytes(buf[..4].try_into().unwrap()) as usize;\n\t\tlet start = 4 + i32::from_ne_bytes(buf[4..8].try_into().unwrap()) as i64;\n\t\tlet end = start + u32::from_ne_bytes(buf[8..12].try_into().unwrap()) as i64;\n\t\tlet name = buf\n\t\t\t.get(..len)\n\t\t\t.and_then(|b| b.get(start as usize..end as usize))\n\t\t\t.and_then(|b| CStr::from_bytes_with_nul(b).ok())\n\t\t\t.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, \"Invalid filename attribute\"))?;\n\n\t\tOk(OsString::from_vec(name.to_bytes().to_vec()))\n\t}\n}\n","sourceCodeStart":21,"sourceCodeEnd":44,"githubUrl":"https://github.com/sxyazi/yazi/blob/a541adde4e5b082719a0f21a87eb3eab071ece58/yazi-fs/src/casefold/macos.rs#L21-L44","documentation":"On macOS, casefold::final_name reads a filename attribute via getattrlist and validates the returned buffer's bounds and NUL-terminated C string. If the offsets/length in the attribute buffer don't describe a valid in-bounds NUL-terminated name, it throws 'Invalid filename attribute'.","triggerScenarios":"The getattrlist ATTR_CMN_RETURNED_ATTRS buffer layout is unexpected — wrong buffer size, kernel returns fewer bytes, or offsets point outside the buffer.","commonSituations":"Running on macOS versions where the attribute layout differs; filesystems (non-APFS/HFS+) that don't fully support the attribute; racing file deletion between the call and attribute read.","solutions":["Retry the lookup — the file may have changed between calls","Check the filesystem type; use APFS/HFS+ which support case-insensitive name recovery","Fall back to a directory-listing case match when the attribute is unavailable"],"exampleFix":"// before\nlet name = buf.get(..len)...ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, \"Invalid filename attribute\"))?;\n// after\nlet name = match buf.get(..len).and_then(|b| b.get(start as usize..end as usize)).and_then(|b| CStr::from_bytes_with_nul(b).ok()) {\n    Some(n) => n,\n    None => return fallback_list_dir_case_match(path), // graceful fallback\n};","handlingStrategy":"fallback","validationCode":"// macOS only; verify the fs supports the attribute before trusting it\nfn supports_returned_attrs() -> bool { std::env::consts::OS == \"macos\" }","typeGuard":"fn valid_name_attr(buf: &[u8], len: usize) -> Option<&std::ffi::CStr> {\n    let start = 4 + i32::from_ne_bytes(buf[4..8].try_into().ok()?) as usize;\n    let end = start + u32::from_ne_bytes(buf[8..12].try_into().ok()?) as usize;\n    buf.get(..len)?.get(start..end).and_then(|b| std::ffi::CStr::from_bytes_with_nul(b).ok())\n}","tryCatchPattern":"match Casefold::final_name(path) {\n    Ok(name) => name,\n    Err(_) => scan_parent_dir_for_case_match(path).unwrap_or_else(|_| path.as_os_str().to_owned()),\n}","preventionTips":["Prefer APFS/HFS+ volumes where the attribute is reliable","Treat the attribute as a hint, not a guarantee","Retry once on transient failures (file being replaced)"],"tags":["macos","filesystem","casefold"],"backgroundTag":"unexpected-response-shape","analyzedSha":"a541adde4e5b082719a0f21a87eb3eab071ece58","analyzedAt":"2026-09-18T10:12:31.659Z","contentChangedAt":"2026-09-18T10:12:31.659Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}