spacedriveapp/spacedrive · error · anyhow::Error

Failed to open stream: {}

Error message

Failed to open stream: {}

What it means

conn.open_uni() failed on the connection used for sending. Because this path reuses cached connections checked with close_reason().is_none(), the typical cause is the connection dying after that liveness check (race), or the peer not accepting unidirectional streams for SYNC_ALPN.

Source

Thrown at core/src/service/network/transports/sync.rs:127

			}

			// Track outbound connection so we can receive incoming streams on 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 a unidirectional stream and send the message
		let mut send = conn
			.open_uni()
			.await
			.map_err(|e| anyhow::anyhow!("Failed to open stream: {}", e))?;

		// Write length prefix (required by multiplexer)
		let len = bytes.len() as u32;
		send.write_all(&len.to_be_bytes())
			.await
			.map_err(|e| anyhow::anyhow!("Failed to write length prefix: {}", e))?;

		// Write message bytes
		send.write_all(&bytes).await.map_err(|e| {
			warn!(
				device_uuid = %target_device,
				error = %e,
				"Failed to write sync message to stream"
			);
			anyhow::anyhow!("Failed to write message: {}", e)
		})?;

		send.finish()

View on GitHub (pinned to 6dfeccf211)

Solutions

  1. On failure, evict the cache entry and retry once with a fresh connection
  2. Treat open_uni errors on cached connections as a cache-invalidation signal
  3. Verify the peer accepts length-prefixed uni streams (the multiplexer depends on them)

Example fix

// before
let mut send = conn.open_uni().await
    .map_err(|e| anyhow::anyhow!("Failed to open stream: {}", e))?;

// after
let mut send = match conn.open_uni().await {
    Ok(s) => s,
    Err(_) => {
        active_connections.write().await.remove(&cache_key); // evict stale entry
        let conn = endpoint.connect(node_id, SYNC_ALPN).await?;
        conn.open_uni().await?
    }
};
Defensive patterns

Strategy: retry

Validate before calling

// Shrink the race before using a cached connection
let conn = {
    let connections = active_connections.read().await;
    match connections.get(&cache_key) {
        Some(c) if c.close_reason().is_none() => c.clone(),
        _ => return reconnect_and_send(device, node_id, bytes).await,
    }
};

Try / catch

match conn.open_uni().await {
    Ok(send) => send,
    Err(_) => {
        // cached connection died after the liveness check: evict and reconnect once
        active_connections.write().await.remove(&cache_key);
        let conn = endpoint.connect(node_id, SYNC_ALPN).await?;
        conn.open_uni().await?
    }
}

Prevention

When it happens

Trigger: Cached connection died between the liveness check and open_uni; peer idle-timeout; peer restarted with the same NodeId; peer accept loop stopped so uni streams are refused.

Common situations: Long-lived daemons holding stale cached connections; laptop sleep/wake invalidating connections underneath the cache.

Related errors


AI-assisted analysis of spacedriveapp/spacedrive@6dfeccf211 (2026-08-16). Data as JSON: /api/errors/fadf2be504eb1394. Report an issue: GitHub.