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

  1. Ensure each selector string is declared exactly once with one type — share it via a public constant instead of re-declaring it
  2. Use `Command::get_unchecked` only if you are certain of the payload type; prefer fixing the type mismatch
  3. Rename the selector string if its payload type genuinely changed so old and new do not collide
  4. 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

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


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)