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
- Inspect the Attrs being set and drop/normalize fields the SFTP backend doesn't support before calling
- Validate permission/timestamp values (mode within 0o7777, sane times) on the caller side
- Only call set_attrs on SFTP files with attributes sourced from that same remote (via stat/Cha)
- 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
- Only set attributes obtained from the same remote (via stat/Cha), not from local files
- Restrict Attrs to fields SFTP supports (perm, times); drop uid/gid or others when unsure
- Range-check modes (0..0o7777) and timestamps before calling
- Treat metadata propagation between local and remote as lossy — wrap in pcall/Result and continue
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.