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
- Fix the underlying cause reported in the message: check host reachability, SSH auth, and known_hosts for backend errors
- Increase the SFTP connection pool timeout/size or reduce concurrency if you hit Timeout
- Ensure all pool usage happens inside the tokio runtime (NoRuntimeSpecified)
- 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
- Retry only Timeout errors with backoff; Backend errors need credential/host fixes first
- Keep concurrency within the pool size or raise the pool limit/timeout in config
- Always run pool operations inside the tokio runtime
- Pre-warm a connection when a remote mount is opened to surface auth issues early
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
- Not a SFTP URL: {url:?}
- Connection closed before receiving reply
- {e}
- Cannot convert attributes
- Trash not supported
AI-assisted analysis of sxyazi/yazi@5f901b886b (2026-09-02).
Data as JSON: /api/errors/7a023b9cc9f76d9e.
Report an issue: GitHub.