spacedriveapp/spacedrive · error · anyhow::Error
Failed to open bidirectional stream: {}
Error message
Failed to open bidirectional stream: {} What it means
conn.open_bi() failed when opening the bidirectional QUIC stream that carries the length-prefixed sync exchange. The connection handle existed, usually pulled from the cache keyed by (node_id, SYNC_ALPN), but the underlying connection was already closed or lost, or the peer refused a new stream.
Source
Thrown at core/src/service/network/transports/sync.rs:257
}
// Track it
if let Some(cmd_sender) = self.command_sender() {
use crate::service::network::core::event_loop::EventLoopCommand;
let _ = cmd_sender.send(EventLoopCommand::TrackOutboundConnection {
node_id,
conn: new_conn.clone(),
});
}
new_conn
};
// Open bidirectional stream
let (mut send, mut recv) = conn
.open_bi()
.await
.map_err(|e| anyhow::anyhow!("Failed to open bidirectional stream: {}", e))?;
// Serialize and send request
let req_bytes = serde_json::to_vec(&request)
.map_err(|e| anyhow::anyhow!("Failed to serialize sync request: {}", e))?;
let len = req_bytes.len() as u32;
send.write_all(&len.to_be_bytes())
.await
.map_err(|e| anyhow::anyhow!("Failed to send length: {}", e))?;
send.write_all(&req_bytes)
.await
.map_err(|e| anyhow::anyhow!("Failed to send request: {}", e))?;
// Properly close send stream
send.finish()
.map_err(|e| anyhow::anyhow!("Failed to finish stream: {}", e))?;
debug!("Sync request sent, waiting for response...");View on GitHub (pinned to 6dfeccf211)
Solutions
- On this error, remove the cache_key from active_connections and retry once with a freshly connected endpoint.connect(...)
- Re-check close_reason() immediately before open_bi and treat any Some(_) as a cache miss
- Bound the retry to one attempt so a dead peer cannot cause a connect loop
Example fix
// before
let (mut send, mut recv) = conn.open_bi().await.map_err(|e| anyhow::anyhow!("Failed to open bidirectional stream: {}", e))?;
// after
let (mut send, mut recv) = match conn.open_bi().await {
Ok(pair) => pair,
Err(_) => {
active_connections.write().await.remove(&cache_key);
let conn = endpoint.connect(node_id, SYNC_ALPN).await?;
conn.open_bi().await?
}
}; Defensive patterns
Strategy: retry
Validate before calling
// Treat any closed cached connection as a cache miss before streaming
let usable = connections.get(&cache_key).is_some_and(|c| c.close_reason().is_none());
if !usable {
active_connections.write().await.remove(&cache_key);
} Try / catch
match conn.open_bi().await {
Err(_) => {
active_connections.write().await.remove(&cache_key);
let conn = endpoint.connect(node_id, SYNC_ALPN).await?;
conn.open_bi().await
}
ok => ok,
} Prevention
- Always evict the cache entry when a stream or connection error occurs
- Bound retries to one reconnect attempt per request
- Re-check close_reason immediately before reusing a cached connection
When it happens
Trigger: Reusing a cached connection whose close_reason() was still None but that the peer closed a moment later (race between the cache check and open_bi); connection dropped by idle timeout; peer hit its concurrent bidirectional stream limit.
Common situations: Long-lived daemons with a warm connection cache; laptop peers sleeping and waking; mobile peers cycling networks.
Related errors
- Failed to open bidirectional stream: {}
- Failed to send length: {}
- Failed to send request: {}
- Failed to read response length: {}
- Failed to read response: {}
AI-assisted analysis of spacedriveapp/spacedrive@6dfeccf211 (2026-08-16).
Data as JSON: /api/errors/accfb86a3e11f157.
Report an issue: GitHub.