linebender/druid · error
The selector " " exists twice with different types. See…
Error message
The selector "{}" exists twice with different types. See druid::Command::get for more information What it means
This panic comes from `Command::get<T>` in druid's command.rs. Selectors are interned once (a `Selector<T>` with a unique symbol), but a selector string can be recreated with a different type parameter, producing two `Selector` values sharing the same symbol with different payload types. When `get` matches on the symbol but the stored payload cannot downcast to `T`, the library panics because it cannot satisfy the type contract.
Solutions
- Ensure each selector string is declared exactly once with one type — share it via a public constant instead of re-declaring it
- Use `Command::get_unchecked` only if you are certain of the payload type; prefer fixing the type mismatch
- Rename the selector string if its payload type genuinely changed so old and new do not collide
- Search the codebase for duplicate `Selector::new("...")` calls with the reported symbol name
Example fix
// before
// module_a.rs
pub const SET_SIZE: Selector<u32> = Selector::new("app.set-size");
// module_b.rs
pub const SET_SIZE: Selector<String> = Selector::new("app.set-size"); // duplicate symbol, different type
// after
// shared.rs
pub const SET_SIZE: Selector<u32> = Selector::new("app.set-size");
// both modules use shared::SET_SIZE Defensive patterns
Strategy: validation
Validate before calling
// Ensure each selector string is declared exactly once; check with a test:
#[test]
fn selectors_are_unique() {
let a = Selector::new("app.set-size") as Selector<u32>;
let b = module_b::SET_SIZE;
assert!(a.symbol() != b.symbol() || std::any::TypeId::of::<u32>() == std::any::TypeId::of::<String>(), "duplicate selector with different type");
}
Type guard
// Narrow safely instead of panicking get:
fn get_checked<T: Any>(cmd: &Command, selector: Selector<T>) -> Option<&T> {
cmd.get(selector) // wraps downcast; ensure only ONE Selector::new per string exists
}
// Prefer checking payload type first:
cmd.payload.is::<T>()
Try / catch
// Rust panics are not catchable in normal code; only if unavoidable:
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
command.get(MY_SELECTOR).cloned()
}));
match result {
Ok(v) => { /* payload matched */ }
Err(_) => eprintln!("selector declared twice with different types"),
}
Prevention
- Declare every selector once in a shared module and import it everywhere
- Never re-create a selector string with a different payload type
- grep for duplicate Selector::new calls with the same string when panics mention a symbol
- When changing a payload type, also change the selector string to invalidate stale postings
- Add a unit test asserting selector symbol uniqueness across the crate
When it happens
Trigger: Two modules each declare `Selector::new("my.command")` with different payload types (e.g. one `Selector<u32>`, one `Selector<String>`), then a command posted with one type is read with `get` using the other; re-declaring the same selector string in a refactor that changed its payload type.
Common situations: Copy-pasting a selector name across crates/modules with different payload generics; renaming/refactoring payload types without changing the selector string; hot-reload or dynamic loading creating two interned symbols; reading a command with the wrong generic in a `Widget::event` handler.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Expected selector " " but the command was " ".
- {}
- TabsPolicy::Build called on a policy that does not support…
- commands should be dispatched via dispatch_cmd
- command must carry a ContextMenu .
AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10).
Data as JSON: /api/errors/9470797a7fd44843.
Report an issue: GitHub.
Appendix: source
Thrown at druid/src/command.rs:477
}
/// Returns `Some(&T)` (this `Command`'s payload) if the selector matches.
///
/// Returns `None` when `self.is(selector) == false`.
///
/// Alternatively you can check the selector with [`is`] and then use [`get_unchecked`].
///
/// # Panics
///
/// Panics when the payload has a different type, than what the selector is supposed to carry.
/// This can happen when two selectors with different types but the same key are used.
///
/// [`is`]: #method.is
/// [`get_unchecked`]: #method.get_unchecked
pub fn get<T: Any>(&self, selector: Selector<T>) -> Option<&T> {
if self.symbol == selector.symbol() {
Some(self.payload.downcast_ref().unwrap_or_else(|| {
panic!(
"The selector \"{}\" exists twice with different types. See druid::Command::get for more information",
selector.symbol()
);
}))
} else {
None
}
}
/// Returns a reference to this `Command`'s payload.
///
/// If the selector has already been checked with [`is`], then `get_unchecked` can be used safely.
/// Otherwise you should use [`get`] instead.
///
/// # Panics
///
/// Panics when `self.is(selector) == false`.
///View on GitHub (pinned to 0f8b1195e4)