spacedriveapp/spacedrive · error · anyhow::Error

Failed to connect to

Error message

Failed to connect to {}: {}

What it means

endpoint.connect(node_id.into(), SYNC_ALPN) failed inside send_sync_request. Iroh must resolve the peer NodeId to reachable addresses (discovery, relays, cached direct addresses) and complete a QUIC/TLS handshake for the sync ALPN; the appended text is the underlying iroh error. It means the peer was unreachable at the transport level, not that sync logic failed.

Solutions

  1. Confirm the peer is online and paired (presence/heartbeat) before initiating sync
  2. Verify iroh relay and discovery configuration on both peers
  3. Retry with backoff - relay path establishment is often transient
  4. Re-pair the devices if the peer reset its identity (new NodeId)
  5. Ensure both devices run versions that agree on SYNC_ALPN

Example fix

// before
let resp = net.send_sync_request(device, req).await?;

// after
let resp = match net.send_sync_request(device, req.clone()).await {
    Ok(resp) => resp,
    Err(e) if is_connect_failure(&e) => {
        tokio::time::sleep(std::time::Duration::from_secs(2)).await;
        net.send_sync_request(device, req).await? // single retry
    }
    Err(e) => return Err(e),
};
Defensive patterns

Strategy: retry

Validate before calling

// Best-effort pre-check: skip peers not marked online
if !device_presence.is_online(device) {
    return Ok(None);
}
net.send_sync_request(device, request).await?;

Type guard

fn is_connect_failure(err: &anyhow::Error) -> bool {
    err.to_string().starts_with("Failed to connect to")
}

Try / catch

let mut attempt = 0;
loop {
    match net.send_sync_request(device, request.clone()).await {
        Ok(resp) => break Ok(resp),
        Err(e) if is_connect_failure(&e) && attempt < 2 => {
            attempt += 1;
            tokio::time::sleep(std::time::Duration::from_secs(2u64 * attempt)).await;
        }
        Err(e) if is_connect_failure(&e) => {
            mark_device_offline(device);
            break Err(e);
        }
        Err(e) => break Err(e),
    }
}

Prevention

When it happens

Trigger: Target device powered off or asleep; peer NodeId stale after an identity reset; no relay/discovery configured so the NodeId cannot be resolved to addresses; both peers behind restrictive NATs; ALPN mismatch because the peer runs a different spacedrive version.

Common situations: Syncing to a laptop that went to sleep; peer changed network leaving only stale address records; one device upgraded so SYNC_ALPN changed; air-gapped or relay-blocked networks.

Related errors


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

Appendix: source

Thrown at core/src/service/network/protocol/sync/transport.rs:153

			library_id = %request.library_id(),
			"Sending sync request"
		);

		// Get endpoint
		let endpoint = self
			.endpoint
			.as_ref()
			.ok_or_else(|| anyhow::anyhow!("Network endpoint not initialized"))?;

		// Connect with SYNC_ALPN
		let conn = endpoint.connect(node_id.into(), SYNC_ALPN).await.map_err(|e| {
			warn!(
				device_uuid = %target_device,
				node_id = %node_id,
				error = %e,
				"Failed to connect to device for sync request"
			);
			anyhow::anyhow!("Failed to connect to {}: {}", target_device, e)
		})?;

		// 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

View on GitHub (pinned to 6dfeccf211)