sxyazi/yazi · error · io::Error

Cannot convert attributes

Error message

Cannot convert attributes

What it means

`set_attrs` converts the engine-agnostic `yazi_fs::engine::Attrs` into SFTP `setstat` attributes; the TryFrom conversion returns `()` on failure, which is mapped to this InvalidInput error. It means the requested attribute set contains values the SFTP backend cannot represent or convert (e.g. unsupported or out-of-range fields), so nothing was sent to the server.

Source

Thrown at yazi-vfs/src/engine/sftp/sftp.rs:172

		match op.rename_posix(self.path, &to).await {
			Ok(()) => {}
			Err(yazi_sftp::Error::Unsupported) => {
				match op.remove(&to).await.map_err(io::Error::from) {
					Ok(()) => {}
					Err(e) if e.kind() == io::ErrorKind::NotFound => {}
					Err(e) => Err(e)?,
				}
				op.rename(self.path, &to).await?;
			}
			Err(e) => Err(e)?,
		}
		Ok(())
	}

	async fn set_attrs(&self, attrs: yazi_fs::engine::Attrs) -> io::Result<()> {
		let attrs = super::Attrs(attrs)
			.try_into()
			.map_err(|()| io::Error::new(io::ErrorKind::InvalidInput, "Cannot convert attributes"))?;

		Ok(self.op().await?.setstat(self.path, attrs).await?)
	}

	async fn symlink<S, F>(&self, original: S, _is_dir: F) -> io::Result<()>
	where
		S: AsStrand,
		F: AsyncFnOnce() -> io::Result<bool>,
	{
		let original = original.as_strand().encoded_bytes();

		Ok(self.op().await?.symlink(original, self.path).await?)
	}

	async fn symlink_metadata(&self) -> io::Result<yazi_fs::cha::Cha> {
		let attrs = self.op().await?.lstat(self.path).await?;
		Ok(Cha::try_from((self.path.file_name().unwrap_or_default(), &attrs))?.0)
	}

View on GitHub (pinned to 8ebf930f17)

Solutions

  1. Inspect the Attrs being set and drop/normalize fields the SFTP backend doesn't support before calling
  2. Validate permission/timestamp values (mode within 0o7777, sane times) on the caller side
  3. Only call set_attrs on SFTP files with attributes sourced from that same remote (via stat/Cha)
  4. Catch the error and degrade gracefully — attribute setting is usually optional

Example fix

-- before
file:set_attrs(all_attrs) -- may fail
-- after
local attrs = { perm = all_attrs.perm } -- only SFTP-representable fields
local ok, err = pcall(file.set_attrs, file, attrs)
Defensive patterns

Strategy: validation

Validate before calling

local attrs = { perm = 0o644, atime = os.time(), mtime = os.time() }
assert(attrs.perm >= 0 and attrs.perm <= 0o7777, 'perm out of range for setstat')

Try / catch

-- set_attrs is best-effort; degrade gracefully
local ok, err = pcall(file.set_attrs, file, attrs)
if not ok then
  ya.dbg('attribute update skipped: '..tostring(err)) -- 'Cannot convert attributes'
end

Prevention

When it happens

Trigger: Setting attributes (permissions, times, uid/gid) on a remote file where the provided Attrs cannot be translated into SFTP setstat fields — for example unsupported attribute kinds or values out of the protocol's range.

Common situations: Plugins applying local-only attributes to remote files, syncing metadata between a local and SFTP location where the local side has attributes SFTP lacks, or calling set_attrs with default/uninitialized Attrs.

Related errors


AI-assisted analysis of sxyazi/yazi@8ebf930f17 (2026-09-02). Data as JSON: /api/errors/f1b681200ab4fc8a. Report an issue: GitHub.