crossbeam-rs/crossbeam · error

dropped `SelectedOperation` without completing the operation

Error message

dropped `SelectedOperation` without completing the operation

What it means

`SelectedOperation` represents an in-progress select: once `select()` returned an operation, you must finish it by calling `send()`, `recv()`, or `recv_ref()` on it. `SelectedOperation` deliberately has no infallible Drop impl, so dropping it without completing the operation is treated as a bug and panics, because the channel's internal state was already marked as selected and would be left inconsistent.

Solutions

  1. Ensure every code path that obtains a `SelectedOperation` calls `send()`/`recv()` on it exactly once
  2. Move error-returning code before the `select()` call, or wrap the completion in an inner scope so the operation is always consumed before `?`/return
  3. Use `select!` macro instead of the manual API — it completes the operation for you
  4. Catch and resume-unwrap is NOT viable for this panic; restructure instead

Example fix

// before
let oper = sel.select();
let val = compute()?; // early return drops `oper` -> panic
oper.send(&tx, val);

// after
let val = compute()?; // fallible work BEFORE selecting
let oper = sel.select();
oper.send(&tx, val); // operation always completed
Defensive patterns

Strategy: type-guard

Type guard

fn completed(op: Option<&SelectedOperation<'_>>) -> bool { op.is_none() } // ensure the operation is consumed via send/recv before its scope ends

Try / catch

// Cannot be caught; restructure instead:
// do fallible work BEFORE sel.select(), then complete the operation immediately

Prevention

When it happens

Trigger: Using `?` or early `return`/`break` in the code path between `sel.select()` and the `send()`/`recv()` completion call; unwinding a panic raised inside the selected-operation handling block; using `std::mem::forget`-like patterns or conditionally skipping the completion call.

Common situations: Error propagation (`?`) in a function whose body contains the select operation; panics inside a closure passed around the `SelectedOperation`; refactors that reorder code so the completion call is no longer reached on all paths.

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 crossbeam-rs/crossbeam@38dacb4622 (2026-09-13). Data as JSON: /api/errors/c602656ef7f8805e. Report an issue: GitHub.

Appendix: source

Thrown at crossbeam-channel/src/select.rs:1375

        assert!(
            r.addr() == self.addr,
            "passed a receiver that wasn't selected",
        );
        let res = unsafe { channel::read(r, &mut self.token) };
        mem::forget(self);
        res.map_err(|_| RecvError)
    }
}

impl fmt::Debug for SelectedOperation<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.pad("SelectedOperation { .. }")
    }
}

impl Drop for SelectedOperation<'_> {
    fn drop(&mut self) {
        panic!("dropped `SelectedOperation` without completing the operation");
    }
}

View on GitHub (pinned to 38dacb4622)