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

  1. Extract the type bits before converting: pass (st_mode & 0o170000) as u16 (S_IFMT mask)
  2. If only permissions are available, OR in an explicit type: 0o100000 | perms for a regular file
  3. 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

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.