sxyazi/yazi · error · io::Error

Cannot write cache stamp: {e}

Error message

Cannot write cache stamp: {e}

What it means

Raised by Stamp::write when persisting a cache-stamp file fails. A stamp is a small file under the URL's auth stamp root (<tmp>/yazi-<uid>/<kind>_<scheme>_<domainhash>/%stamp/<urlhash>) that stores a 26-byte content signature plus the target filename, so yazi can later detect that a remote (non-local) file changed. The original std::io::Error from Local::regular(&path).write(data) is re-wrapped with io::Error::new(e.kind(), ...), so the kind (PermissionDenied, StorageFull, NotFound, ...) is preserved and the {e} suffix carries the real cause.

Source

Thrown at yazi-vfs/src/stamp.rs:61

		let stamp = Self::read_at(&path).await?;
		let name = StrandCow::with(dir.kind(), stamp.name()).map_err(io::Error::other)?;

		let url = dir.try_join(name)?;
		if url.hash_u128_str(&mut [0; Self::SIG_LEN]).as_bytes() != key.encoded_bytes() {
			return Err(io::Error::new(io::ErrorKind::InvalidData, "Cache stamp does not match entry"));
		}

		Ok(url)
	}

	pub async fn write(cha: Cha, url: Url<'_>) -> io::Result<()> {
		let path = url.stamp_entry().ok_or_else(|| io::Error::other("Cannot determine cache stamp"))?;
		let data = Self::encode(cha, url)?;

		Local::regular(&path)
			.write(data)
			.await
			.map_err(|e| io::Error::new(e.kind(), format!("Cannot write cache stamp: {e}")))
	}

	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"));
		}

View on GitHub (pinned to 94abcfa92f)

Solutions

  1. Read the wrapped {e} and e.kind() (preserved by the wrap) to get the true cause before changing anything
  2. Verify the temp dir: make $TMPDIR/$XDG_RUNTIME_DIR an existing writable absolute path, and recreate <tmp>/yazi-<uid> if a cleaner removed it
  3. Free space on the filesystem that hosts the temp dir (StorageFull) and retry the fetch so the stamp is rewritten
  4. Clear the %stamp subtree for that auth so damaged entries get rebuilt from scratch
  5. As a library user, treat a failed stamp write as non-fatal: the cached file itself is intact, only change-detection metadata is missing

Example fix

// before
Stamp::write(cha, url.as_url()).await?; // aborts the whole fetch pipeline on a temp-dir hiccup

// after
if let Err(e) = Stamp::write(cha, url.as_url()).await {
    // stamp is only invalidation metadata; keep the cached file and continue
    tracing::warn!("stamp write failed ({e}); change detection disabled for {url}");
}
Defensive patterns

Strategy: try-catch

Validate before calling

if let Some(stamp) = url.as_url().stamp_entry() {
    if let Some(parent) = stamp.parent() {
        std::fs::create_dir_all(parent)?; // surfaces PermissionDenied / StorageFull before the download
    }
}

Try / catch

match Stamp::write(cha, url.as_url()).await {
    Ok(()) => {}
    Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {
        // temp dir or stamp file unwritable: fix TMPDIR / recreate <tmp>/yazi-<uid>, then retry once
    }
    Err(e) => tracing::warn!("stamp write failed: {e}"), // non-fatal: only invalidation metadata is lost
}

Prevention

When it happens

Trigger: Awaiting Stamp::write(cha, url) for a non-local URL (SFTP, Mount, Hub, Scope) whose stamp_entry() resolves, and the underlying write fails: the yazi temp dir was deleted mid-session, $TMPDIR/$XDG_RUNTIME_DIR is unwritable or missing, the temp filesystem is full (ENOSPC), or the process lacks permission on an existing stamp file.

Common situations: Long-running session whose /tmp was purged by tmpreaper/systemd-tmpfiles; TMPDIR pointed at a nonexistent or read-only path in a container; disk full on the tmpfs; embedding yazi-vfs in a service with a restricted temp directory.

Related errors


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