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
- Ensure every code path that obtains a `SelectedOperation` calls `send()`/`recv()` on it exactly once
- Move error-returning code before the `select()` call, or wrap the completion in an inner scope so the operation is always consumed before `?`/return
- Use `select!` macro instead of the manual API — it completes the operation for you
- 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
- Never use `?` or early return between select() and send()/recv()
- Complete the operation in the same scope it was created
- Prefer the select! macro over the manual SelectedOperation API
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
- no operations have been added to `Select`
- bounded channel capacity is too large
- queue capacity is too large
- failed to spawn scoped thread
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)