sxyazi/yazi · error · io::Error

{e}

Error message

{e}

What it means

This error surfaces connection-establishment failures from the SFTP connection pool (`deadpool`) in `roll`. Pool timeouts become ErrorKind::TimedOut, backend errors are passed through unchanged, and everything else (pool closed, no runtime, post-create hook failure) becomes a generic io::Error with the pool error's text. It wraps whatever prevented obtaining a pooled SFTP connection.

Source

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

}

impl Conn {
	pub(super) async fn roll(self) -> io::Result<deadpool::managed::Object<Self>> {
		use deadpool::managed::PoolError;

		let pool = *super::CONN.lock().entry(self.config).or_insert_with(|| {
			Box::leak(Box::new(
				deadpool::managed::Pool::builder(self)
					.runtime(deadpool::Runtime::Tokio1)
					.max_size(8)
					.create_timeout(Some(Duration::from_secs(45)))
					.build()
					.unwrap(),
			))
		});

		pool.get().await.map_err(|e| match e {
			PoolError::Timeout(_) => io::Error::new(io::ErrorKind::TimedOut, e.to_string()),
			PoolError::Backend(e) => e,
			PoolError::Closed | PoolError::NoRuntimeSpecified | PoolError::PostCreateHook(_) => {
				io::Error::other(e.to_string())
			}
		})
	}

	async fn connect(self) -> Result<russh::Channel<russh::client::Msg>, russh::Error> {
		let pref = Arc::new(russh::client::Config {
			inactivity_timeout: Some(std::time::Duration::from_secs(60)),
			keepalive_interval: Some(std::time::Duration::from_secs(10)),
			..Default::default()
		});

		let session = if self.config.password.is_some() {
			self.connect_by_password(pref).await
		} else if !self.config.key_file.as_os_str().is_empty()
			&& !self.config.cert_file.as_os_str().is_empty()

View on GitHub (pinned to 5f901b886b)

Solutions

  1. Fix the underlying cause reported in the message: check host reachability, SSH auth, and known_hosts for backend errors
  2. Increase the SFTP connection pool timeout/size or reduce concurrency if you hit Timeout
  3. Ensure all pool usage happens inside the tokio runtime (NoRuntimeSpecified)
  4. Retry on TimedOut with backoff — transient network slowness is the most common cause

Example fix

// before
let conn = pool.get().await?; // Timeout on slow network
// after
match pool.get().await {
    Ok(c) => c,
    Err(PoolError::Timeout(_)) => return Err(io::Error::new(TimedOut, "sftp pool timeout, retry")),
    Err(e) => return Err(map_pool_err(e)),
}
Defensive patterns

Strategy: retry

Validate before calling

// Reachability pre-check before issuing SFTP work
let ok = tokio::net::TcpStream::connect((host, port)).await.is_ok();
if !ok { return Err(io::Error::new(io::ErrorKind::TimedOut, "host unreachable")); }

Try / catch

match pool.get().await {
    Err(PoolError::Timeout(_)) => backoff_retry(|| pool.get()).await,
    Err(PoolError::Backend(e)) => return Err(e), // auth/host problem — don't blind-retry
    Err(other) => return Err(io::Error::other(other.to_string())),
    Ok(conn) => conn,
}

Prevention

When it happens

Trigger: Acquiring a connection to a remote when the pool is exhausted (more concurrent ops than pool limit and the wait times out), the pool is shut down, the connection factory (backend) fails (bad host/key/auth), or the pool is used outside a tokio runtime.

Common situations: Slow or unreachable SSH host causing pool timeout, wrong SSH credentials/known_hosts making every Backend creation fail, too many parallel file operations saturating the pool, or dropping the tokio runtime while a connection is awaited.

Related errors


AI-assisted analysis of sxyazi/yazi@5f901b886b (2026-09-02). Data as JSON: /api/errors/7a023b9cc9f76d9e. Report an issue: GitHub.