sxyazi/yazi · error · anyhow::Error
invalid file mode: {value:04o}
Error message
invalid file mode: {value:04o} What it means
ChaMode is a bitflags type over the file-type portion of the Unix st_mode (T_FILE, T_DIR, T_LINK, T_BLOCK, T_CHAR, T_SOCK, T_FIFO). TryFrom<u16> first round-trips the value through from_bits — "invalid file mode" means the u16 contains bits outside the defined ChaMode flags — and then requires the type bits (T_MASK) to match exactly one known type, bailing "invalid file type" otherwise.
Source
Thrown at yazi-fs/src/cha/mode.rs:66
fn deref(&self) -> &Self::Target {
match *self & Self::T_MASK {
Self::T_FILE => &ChaType::File,
Self::T_DIR => &ChaType::Dir,
Self::T_LINK => &ChaType::Link,
Self::T_BLOCK => &ChaType::Block,
Self::T_CHAR => &ChaType::Char,
Self::T_SOCK => &ChaType::Sock,
Self::T_FIFO => &ChaType::FIFO,
_ => &ChaType::Unknown,
}
}
}
impl TryFrom<u16> for ChaMode {
type Error = anyhow::Error;
fn try_from(value: u16) -> Result<Self, Self::Error> {
let me = Self::from_bits(value).ok_or_else(|| anyhow!("invalid file mode: {value:04o}"))?;
match me & Self::T_MASK {
Self::T_FILE
| Self::T_DIR
| Self::T_LINK
| Self::T_BLOCK
| Self::T_CHAR
| Self::T_SOCK
| Self::T_FIFO => Ok(me),
_ => bail!("invalid file type: {value:04o}"),
}
}
}
#[cfg(unix)]
impl From<ChaMode> for std::fs::Permissions {
fn from(value: ChaMode) -> Self {
use std::os::unix::fs::PermissionsExt;
View on GitHub (pinned to 94abcfa92f)
Solutions
- Extract the type bits before converting: pass (st_mode & 0o170000) as u16 (S_IFMT mask)
- If only permissions are available, OR in an explicit type: 0o100000 | perms for a regular file
- If a legitimate platform bit is rejected, extend the ChaMode bitflags definition to cover it
Example fix
// before: full st_mode, permission bits rejected by from_bits let mode = ChaMode::try_from(st.st_mode as u16)?; // after: type bits only (S_IFMT) let mode = ChaMode::try_from((st.st_mode & 0o170000) as u16)?;
Defensive patterns
Strategy: type-guard
Validate before calling
// Extract only the type bits (S_IFMT) before converting: const S_IFMT: u16 = 0o170000; let mode = ChaMode::try_from(st.st_mode & S_IFMT as u32 as u16)?;
Type guard
const S_IFMT: u16 = 0o170000;
fn is_valid_file_mode(value: u16) -> bool {
matches!(
value & S_IFMT,
0o100000 | 0o040000 | 0o120000 | 0o060000 | 0o020000 | 0o140000 | 0o010000
)
} Try / catch
match ChaMode::try_from(raw) {
Ok(m) => m,
Err(e) if e.to_string().starts_with("invalid file mode") => ChaMode::try_from(raw & 0o170000)?,
Err(e) => return Err(e),
} Prevention
- Always mask with S_IFMT (0o170000) before TryFrom<u16>
- Never feed a full st_mode including permission bits
- Unit-test the conversion for every file type you support
When it happens
Trigger: Passing a full st_mode that includes permission bits not modeled in ChaMode's flag set, or a permission-only value like 0o644 whose type bits are zero, leaving the T_MASK match with no valid variant.
Common situations: Code or plugins building Cha by hand from libc stat output; test fixtures with arbitrary modes; platform mode values containing bits (sticky/setuid) absent from the bitflags definition.
Related errors
AI-assisted analysis of sxyazi/yazi@94abcfa92f (2026-08-16).
Data as JSON: /api/errors/213fb4915cd73200.
Report an issue: GitHub.