spacedriveapp/spacedrive · error · anyhow::Error

No paired devices found. Pair a device first with: sd networ

Error message

No paired devices found.
Pair a device first with:
sd network pair generate  # on this device
sd network pair join <code>  # on the other device

What it means

Thrown by the interactive 'sd library sync' wizard (step 2) after it queries the daemon with ListPairedDevicesInput { connected_only: false } and receives an empty devices list. It aborts the wizard before any device selection because library sync requires a peer device. The message itself tells you the pairing commands to run.

Source

Thrown at apps/cli/src/domains/library/mod.rs:307

	let library_idx = select("Select local library to sync", &library_choices)?;
	let local_library_id = libraries[library_idx].id;

	println!(
		"\n✓ Selected local library: {}\n",
		libraries[library_idx].name
	);

	// Step 2: Select remote device from paired devices
	let paired_devices: ListPairedDevicesOutput = execute_core_query!(
		ctx,
		ListPairedDevicesInput {
			connected_only: false
		}
	);

	if paired_devices.devices.is_empty() {
		anyhow::bail!(
			"No paired devices found.\n\
			Pair a device first with:\n\
			  sd network pair generate  # on this device\n\
			  sd network pair join <code>  # on the other device"
		);
	}

	let device_choices: Vec<String> = paired_devices
		.devices
		.iter()
		.map(|d| {
			let status = if d.is_connected {
				"connected"
			} else {
				"paired"
			};
			format!("{} - {} ({})", d.name, d.os_version, status)
		})

View on GitHub (pinned to 6dfeccf211)

Solutions

  1. Run 'sd network pair generate' on this device to create a pairing code
  2. On the other device run 'sd network pair join <code>' with that code
  3. Confirm the pairing took effect by listing paired devices before retrying 'sd library sync'
  4. If pairing was done previously, verify the daemon is running against the expected data directory (pairing records are stored there)

Example fix

// before: wizard discovers empty pairing list and bails mid-flow
if paired_devices.devices.is_empty() {
    anyhow::bail!("No paired devices found. ...");
}

// after: check pairing up-front so the user is told before answering prompts
let paired_devices: ListPairedDevicesOutput = execute_core_query!(
    ctx,
    ListPairedDevicesInput { connected_only: false }
);
if paired_devices.devices.is_empty() {
    println!("No paired devices found. Pair one first:");
    println!("  sd network pair generate");
    return Ok(()); // graceful exit instead of hard bail
}
Defensive patterns

Strategy: validation

Validate before calling

// Before launching the wizard, ask the daemon for paired devices
let paired: ListPairedDevicesOutput = execute_core_query!(
    ctx,
    ListPairedDevicesInput { connected_only: false }
);
if paired.devices.is_empty() {
    eprintln!("Pair a device first: sd network pair generate / sd network pair join <code>");
    return Ok(());
}

Type guard

fn has_paired_devices(out: &ListPairedDevicesOutput) -> bool {
    !out.devices.is_empty()
}

Try / catch

match run_sync_wizard(ctx).await {
    Err(e) if e.to_string().contains("No paired devices") => {
        eprintln!("Skipping sync: {}", e);
        Ok(())
    }
    other => other,
}

Prevention

When it happens

Trigger: Running 'sd library sync' (interactive setup) on a device whose daemon has zero entries in its pairing database. The query is execute_core_query!(ctx, ListPairedDevicesInput { connected_only: false }), so even offline paired devices count; empty means nothing was ever paired.

Common situations: Fresh install before any pairing was done; the daemon data directory was reset or changed (pairings live in daemon state, not in the CLI); testing the wizard against a dev daemon with a clean state.

Related errors


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