AprilNEA/OpenLogi · error

the selected direct-capture target does not match the…

Error message

the selected direct-capture target does not match the profile transport and slot; reconnect the same device and use the same --device selector

What it means

When finishing a contribution, `require_same_structural_route` compares the route of the currently selected device against the route recorded in the resumable profile, ignoring only receiver identity (UID/serial) but requiring the transport kind, slot, and for Direct/RawHid the vendor/product (and usage page/id) to match. A mismatch means the user reconnected a different device or used a different `--device` selector than in the original capture, which would mix identity material from two devices; the run is aborted.

Solutions

  1. Reconnect the exact same device in the same way as the original capture (same transport: receiver slot, Bluetooth, or wired)
  2. Use the same `--device` selector value as the original run — prefer a stable selector (VID:PID or serial) over a mutable numeric index
  3. Check `openlogi list` to confirm the device's current route matches the profile, then rerun the contribution
  4. If the setup legitimately changed, delete the resumable state and restart the contribution from scratch with the new device/transport

Example fix

// before: device moved to another receiver slot between sessions
$ openlogi fixture contribute --id my-fixture --device 2 --output output/my-fixture
Error: the selected direct-capture target does not match the profile transport and slot; ...
// after: same device, same slot, same selector
$ openlogi list   # confirm device is back on the original slot/route
$ openlogi fixture contribute --id my-fixture --device 1 --output output/my-fixture
Defensive patterns

Strategy: validation

Validate before calling

// Compare structural routes (transport + slot + vid/pid) before resuming
fn structural_matches(expected: &DeviceRoute, actual: &DeviceRoute) -> bool {
    use DeviceRoute::*;
    match (expected, actual) {
        (Bolt { slot: a, .. }, Bolt { slot: b }) => a == b,
        (Unifying { slot: a, .. }, Unifying { slot: b }) => a == b,
        (Direct { vendor_id: v1, product_id: p1 }, Direct { vendor_id: v2, product_id: p2 }) => {
            v1 == v2 && p1 == p2
        }
        (RawHid { vendor_id: v1, product_id: p1, .. }, RawHid { vendor_id: v2, product_id: p2, .. }) => {
            v1 == v2 && p1 == p2
        }
        _ => false,
    }
}

Try / catch

match resume_result {
    Err(e) if e.to_string().contains("does not match the profile transport and slot") => {
        eprintln!("reconnect the same device / reuse the original --device selector");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Resuming `openlogi fixture contribute` (or finishing capture) after the device moved to a different receiver slot, was reconnected via a different transport (e.g. Bluetooth instead of the receiver, receiver instead of wired), or a different `--device` selector matched a device with different VID/PID/usage than the recorded profile.

Common situations: Unplugging and replugging the device into a different receiver slot or port between capture sessions; switching the device between Bolt/Unifying receiver and Bluetooth-direct; passing a numeric index to `--device` that now resolves to a different device after replug; capturing multiple similar devices (same vendor, different model/PID) and selecting the wrong one on resume.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of AprilNEA/OpenLogi@e846e6f4b4 (2026-09-13). Data as JSON: /api/errors/2b9eef372afe9342. Report an issue: GitHub.

Appendix: source

Thrown at crates/openlogi-cli/src/cmd/fixture/contribute.rs:438

            DeviceRoute::RawHid {
                vendor_id: right_vendor,
                product_id: right_product,
                usage_page: right_page,
                usage_id: right_usage,
                ..
            },
        ) => {
            left_vendor == right_vendor
                && left_product == right_product
                && left_page == right_page
                && left_usage == right_usage
        }
        _ => false,
    };
    if matches {
        Ok(())
    } else {
        bail!(
            "the selected direct-capture target does not match the profile transport and slot; \
             reconnect the same device and use the same --device selector"
        )
    }
}

fn validate_args(args: &ContributeArgs) -> Result<()> {
    if args.id.trim().is_empty() || matches!(args.id.as_str(), "." | "..") {
        bail!("--id must be a nonempty synthetic path component");
    }
    if Path::new(&args.id).file_name() != Some(OsStr::new(&args.id))
        || args.id.contains('/')
        || args.id.contains('\\')
    {
        bail!("--id must be one synthetic path component without separators");
    }
    if args.name.trim().is_empty() {
        bail!("--name must be a nonempty synthetic device name");

View on GitHub (pinned to e846e6f4b4)