sxyazi/yazi · error · io::Error

revalidated file URL changed

Error message

revalidated file URL changed

What it means

During directory revalidation, entries are re-stat'ed and compared against the cached `File`; if the fresh entry's URL differs from the cached one, this InvalidData error is thrown. Because a file's URL should be stable across a revalidation (same key/urn in the same folder), a change signals corruption or a broken engine `revalidate` implementation rather than a normal rename (renames go through explicit events, not revalidation).

Source

Thrown at yazi-vfs/src/entries.rs:65

		async fn go(entries: &[DirEntry]) -> Vec<File> {
			let mut files = Vec::with_capacity(entries.len());
			for dent in entries {
				files.push(match dent.file().await {
					Ok(file) => file,
					Err(_) => File::from_dummy(dent.url(), dent.file_type().await.ok()),
				});
			}
			files
		}

		let (first, second, third) = join!(go(first), go(second), go(third));
		Ok([first, second, third].into_iter().flatten().collect())
	}

	async fn revalidate(old: &File) -> io::Result<Option<File>> {
		match engine::revalidate(old).await? {
			Some(new) if new.url != old.url => {
				Err(io::Error::new(io::ErrorKind::InvalidData, "revalidated file URL changed"))
			}
			Some(new) if !new.is_dir() => Err(io::ErrorKind::NotADirectory.into()),
			Some(new) => Ok(Some(new)),
			None if PARTITIONS.read().timeless(old.cha) => Ok(Some(old.clone())),
			None => Ok(None),
		}
	}
}

View on GitHub (pinned to 8ebf930f17)

Solutions

  1. Fix the engine's revalidate/metadata implementation to return an entry with the identical URL it was queried with
  2. If the backend canonicalizes names, normalize at the adapter so the URL round-trips unchanged
  3. Check for symlink-following or case-folding in the backend stat path and disable it for keying
  4. Catch the error and drop the stale entry (trigger a full directory reload) instead of failing the whole revalidation

Example fix

// before (in a custom engine's revalidate)
Ok(Some(File::new(canonical_url, cha))) // url changed -> InvalidData
// after
Ok(Some(File::new(old.url.clone(), cha))) // preserve queried URL
Defensive patterns

Strategy: try-catch

Validate before calling

// Engines must return the queried URL unchanged; assert at the adapter boundary
debug_assert_eq!(new.url, old.url, "engine revalidate changed the URL");

Try / catch

match entries::revalidate(old).await {
    Err(e) if e.to_string() == "revalidated file URL changed" => {
        // engine bug or name normalization — drop the stale entry and schedule a full reload
        log::warn!("revalidate changed URL for {:?}; forcing reload", old.url);
        reload_partition(old.url.parent()).await
    }
    r => r,
}

Prevention

When it happens

Trigger: An engine's `revalidate` returns a File whose `url` differs from the input — e.g. a backend normalizing the path differently, changing case, resolving symlinks into the URL, or returning an entry for a different file entirely.

Common situations: Backends that canonicalize or percent-decode the urn during stat, remote servers returning differently-escaped names, or a bug in a custom Lua VFS returning the wrong entry from its metadata/cha hook.

Related errors


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