bevyengine/bevy · critical
can only hash u64 using PassHasher
Error message
can only hash u64 using PassHasher
What it means
`PassHasher` (crates/bevy_platform/src/hash.rs) is a pass-through hasher for keys that are already well-distributed u64 values (bevy `Entity` ids). It only implements `write_u64` (stores the value) and `finish`; hashing anything that emits raw bytes hits `write`, which panics with "can only hash u64 using PassHasher". Hashers built on it (e.g. `NoOpHash`/the Entity-hash state used by `EntityHashMap`/`EntityHashSet`) are only valid for keys whose `Hash` impl is exactly one `write_u64` call.
Source
Thrown at crates/bevy_platform/src/hash.rs:154
PassHasher::default()
}
}
/// A no-op hash that only works on `u64`s. Will panic if attempting to
/// hash a type containing non-u64 fields.
#[derive(Debug, Default)]
pub struct PassHasher {
hash: u64,
}
impl Hasher for PassHasher {
#[inline]
fn finish(&self) -> u64 {
self.hash
}
fn write(&mut self, _bytes: &[u8]) {
panic!("can only hash u64 using PassHasher");
}
#[inline]
fn write_u64(&mut self, i: u64) {
self.hash = i;
}
}
/// [`BuildHasher`] for types that already contain a high-quality hash.
#[derive(Clone, Default)]
pub struct NoOpHash;
impl BuildHasher for NoOpHash {
type Hasher = NoOpHasher;
fn build_hasher(&self) -> Self::Hasher {
NoOpHasher(0)
}View on GitHub (pinned to 396ca72708)
Solutions
- Keep pass-through hashers for `Entity`/`u64` keys only
- For arbitrary keys, use a real hasher: std's `RandomState`, or bevy_platform's ahash-based `RandomState`/`FixedState`
- If you must keep the map, pre-hash your key to a u64 yourself and use the hash as the key
Example fix
// before: &str keys in a pass-through-hashed map -> panic on first insert
let mut map: EntityHashMap<String, u32> = EntityHashMap::default();
map.insert("player".to_string(), 1);
// after: regular hasher for string keys
let mut map: HashMap<String, u32> = HashMap::default();
// or keep EntityHashMap only for Entity/u64 keys Defensive patterns
Strategy: type-guard
Validate before calling
null
Type guard
/// Only types whose Hash impl is exactly one write_u64 may live in
/// pass-through-hashed maps. Restrict usage via this newtype.
struct EntityMap<V> {
inner: EntityHashMap<Entity, V>,
}
impl<V> EntityMap<V> {
fn insert(&mut self, k: Entity, v: V) -> Option<V> {
self.inner.insert(k, v) // Entity -> write_u64, safe
}
fn get(&self, k: Entity) -> Option<&V> { self.inner.get(&k) }
} Prevention
- Treat EntityHashMap/EntityHashSet as Entity-only containers; use std/ahash maps for any other key
- Remember even u32/i32 keys are unsafe here: the default write_u32 forwards to write()
- When refactoring map key types, switch the hasher back to RandomState in the same commit
When it happens
Trigger: Inserting/looking up with a key type that hashes via bytes or non-u64 integers in a map built on this hasher: `&str`, `String`, `u32` (default `write_u32` forwards to `write`), tuples like `(Entity, u32)`, floats, enums, or any composite type. Copying an `EntityHashMap` declaration and swapping the key type is the classic trigger.
Common situations: Refactoring an `EntityHashMap<Entity, T>` into a keyed cache with a different key; using the bevy_platform hash state in a `hashbrown`/`HashMap` generic over `S` and instantiating it with non-Entity keys; serialization code storing string keys in an Entity-keyed map.
Related errors
- B0003
- cannot reserve a larger AlignedVec
- Cannot call `ReflectComponent::reflect_mut` on component {na
- Cannot call `ReflectComponent::reflect_unchecked_mut` on com
- component should represent a type.
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/c38b735f0f45899b.
Report an issue: GitHub.