sxyazi/yazi · error · anyhow::Error

not a File

Error message

not a File

What it means

Data is yazi's dynamic typed value used at Lua/action boundaries. File::try_from(Data) unwraps the contained value with into_any::<File>(); if the Data holds any other type (string, table, different struct), the conversion fails with "not a File". It is a type mismatch at a dynamically-typed boundary, not a filesystem error.

Source

Thrown at yazi-fs/src/file/data.rs:13

use anyhow::{anyhow, bail};
use yazi_macro::impl_data_any;
use yazi_shared::data::Data;

use crate::file::{File, Files};

impl_data_any!(File, from_into_lua = inherit);

impl TryFrom<Data> for File {
	type Error = anyhow::Error;

	fn try_from(value: Data) -> Result<Self, Self::Error> {
		value.into_any::<Self>().ok_or_else(|| anyhow!("not a File"))
	}
}

impl TryFrom<&Data> for File {
	type Error = anyhow::Error;

	fn try_from(value: &Data) -> Result<Self, Self::Error> {
		value.as_any::<Self>().cloned().ok_or_else(|| anyhow!("not a File"))
	}
}

impl TryFrom<Data> for Files {
	type Error = anyhow::Error;

	fn try_from(value: Data) -> Result<Self, Self::Error> {
		let Data::List(files) = value else { bail!("not a list of Files") };
		files.into_iter().map(File::try_from).collect::<Result<_, _>>().map(Self)
	}

View on GitHub (pinned to 94abcfa92f)

Solutions

  1. Pass the actual File value through (e.g. from a Folder's entries) instead of re-wrapping other data
  2. Branch on the contained type before converting (as_any::<File>()) and give a precise error
  3. On the Lua side, keep File userdata intact rather than converting to tables

Example fix

// before
let f = File::try_from(data)?; // data actually holds a Strand

// after
let Some(f) = data.into_any::<File>() else {
    bail!("expected a File, got a different type");
};
Defensive patterns

Strategy: type-guard

Validate before calling

// Check the contained type before consuming:
ensure!(data.as_any::<File>().is_some(), "expected a File");
let f = File::try_from(data)?;

Type guard

fn is_file(d: &Data) -> bool { d.as_any::<File>().is_some() }

Try / catch

match data.into_any::<File>() {
    Some(f) => f,
    None => bail!("expected a File, got a different Data type"),
}

Prevention

When it happens

Trigger: Passing a Data wrapping something else where a File is required: extracting plugin/action arguments, decoding event payloads, or feeding results of generic Data-valued APIs into File::try_from.

Common situations: Lua-side code handing untyped values into Rust helpers; payload shape drift after type changes; plugins re-wrapping File fields into plain tables.


AI-assisted analysis of sxyazi/yazi@94abcfa92f (2026-08-16). Data as JSON: /api/errors/0d5fbf6c274d6288. Report an issue: GitHub.