sxyazi/yazi · warning · io::Error
Remote file has changed since last download
Error message
Remote file has changed since last download
What it means
Returned by Stamp::validate with ErrorKind::InvalidData when the 26-byte signature (Cha hashed via hash_u128_str) stored in the stamp no longer equals the hash of the file's current metadata. This is the intended integrity signal: the remote file was modified after it was downloaded into the local cache, so the cached copy is stale.
Source
Thrown at yazi-vfs/src/stamp.rs:81
fn encode(cha: Cha, url: Url) -> io::Result<Vec<u8>> {
let name = url.name().ok_or_else(|| io::Error::other("URL has no filename"))?;
let mut buf = Vec::with_capacity(Self::SIG_LEN + name.len());
buf.extend_from_slice(cha.hash_u128_str(&mut [0; Self::SIG_LEN]).as_bytes());
buf.extend_from_slice(name.encoded_bytes());
Ok(buf)
}
pub fn validate(&self, cha: Cha, url: Url) -> io::Result<()> {
let name = url.name().ok_or_else(|| io::Error::other("URL has no filename"))?;
if self.name() != name.encoded_bytes() {
return Err(io::Error::new(io::ErrorKind::InvalidData, "Cache stamp does not match target"));
}
if self.sig() != cha.hash_u128_str(&mut [0; Self::SIG_LEN]) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Remote file has changed since last download",
));
}
Ok(())
}
#[inline]
pub fn sig(&self) -> &str { unsafe { str::from_utf8_unchecked(&self.0[..Self::SIG_LEN]) } }
#[inline]
pub fn name(&self) -> &[u8] { &self.0[Self::SIG_LEN..] }
}
impl TryFrom<Vec<u8>> for Stamp {
type Error = io::Error;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {View on GitHub (pinned to 94abcfa92f)
Solutions
- Invalidate the cached copy and re-download the file — this error is designed to trigger exactly that
- Delete the cached entry and its stamp under the auth's cache root and %stamp root, then retry the operation
- If the change is only mtime noise (for example rsync -a touching files), ignore mtime in your own comparison or re-stamp after confirming content is unchanged
- In plugins and wrappers, match ErrorKind::InvalidData from validate() and trigger a fresh fetch instead of surfacing the error to the user
Example fix
// before
stamp.validate(cha, url.as_url())?; // "Remote file has changed since last download" surfaces to the user
// after
match stamp.validate(cha, url.as_url()) {
Ok(()) => use_cached(url).await,
Err(e) if e.kind() == io::ErrorKind::InvalidData => refetch(url).await,
Err(e) => return Err(e),
} Defensive patterns
Strategy: fallback
Try / catch
match stamp.validate(cha, url.as_url()) {
Ok(()) => use_cached(url).await,
Err(e) if e.kind() == io::ErrorKind::InvalidData => refetch(url).await, // remote changed => refresh
Err(e) => return Err(e),
} Prevention
- Expect this error on volatile remote files; always code the re-fetch path alongside the cached path
- For frequently-touched files, refresh the stamp after confirming content equality so mtime noise does not force re-downloads
- Never cache write-heavy remote files without a validate-then-refetch branch
When it happens
Trigger: The remote file behind an SFTP/Hub/Mount URL is edited, replaced, or touched (changing size/mtime captured in Cha) between the download that wrote the stamp and a later validate(cha, url) call; the cached copy then corresponds to an older version of the file.
Common situations: Previewing a remote log or document that is still being written; another client editing the file while yazi holds a cached copy; server-side rsync/touch changing mtime without content changes.
Related errors
- Cache stamp does not match target
- Cannot write cache stamp: {e}
- Invalid cache stamp
- Failed to get cache directory
- entry name is a valid component of the SFTP URL
AI-assisted analysis of sxyazi/yazi@94abcfa92f (2026-08-16).
Data as JSON: /api/errors/9d5c934e8d2c72ee.
Report an issue: GitHub.