pydantic/monty · error

{} python values are not hashable

Error message

{} python values are not hashable

What it means

Fallback panic in MontyObject::hash for any Python value type that has no defined hash (lists, dicts, sets, etc.), mirroring CPython's `unhashable type` behavior but as an unrecoverable Rust panic rather than a TypeError. The message names the offending type via type_name().

Source

Thrown at crates/monty-types/src/object.rs:642

                }
            }
            Self::Float(f) => f.to_bits().hash(state),
            Self::String(string) => string.hash(state),
            Self::Bytes(bytes) => bytes.hash(state),
            Self::Date(date) => date.hash(state),
            Self::DateTime(datetime) => datetime.hash(state),
            Self::Time(time) => time.hash(state),
            Self::TimeDelta(delta) => delta.hash(state),
            Self::TimeZone(timezone) => timezone.hash(state),
            Self::Path(path) => path.hash(state),
            Self::FileHandle(MontyFileHandle { path, mode, position }) => {
                path.hash(state);
                mode.as_str().hash(state);
                position.hash(state);
            }
            Self::Type(t) => t.name().hash(state),
            Self::Cycle(_, _) => panic!("cycle values are not hashable"),
            _ => panic!("{} python values are not hashable", self.type_name()),
        }
    }
}

impl PartialEq for MontyObject {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Ellipsis, Self::Ellipsis) => true,
            (Self::NotImplemented, Self::NotImplemented) => true,
            (Self::None, Self::None) => true,
            (Self::Bool(a), Self::Bool(b)) => a == b,
            (Self::Int(a), Self::Int(b)) => a == b,
            (Self::BigInt(a), Self::BigInt(b)) => a == b,
            // Cross-compare Int and BigInt without allocating a temporary BigInt.
            (Self::Int(a), Self::BigInt(b)) | (Self::BigInt(b), Self::Int(a)) => b.to_i64() == Some(*a),
            // Use to_bits() for float comparison to be consistent with Hash
            (Self::Float(a), Self::Float(b)) => a.to_bits() == b.to_bits(),
            (Self::String(a), Self::String(b)) => a == b,

View on GitHub (pinned to adc986b362)

Solutions

  1. Only hash hashable variants: check the type (e.g. matches!(v, MontyObject::Int(_) | MontyObject::Str(_), ...)) before hashing
  2. Use a total, structural hash you control (e.g. hash of repr/JSON) for containers instead of this method
  3. If a legitimate type hits this arm, add an explicit hash arm in crates/monty-types/src/object.rs for it

Example fix

// before
map.insert(unhashable_value, data); // panics: list python values are not hashable

// after
if is_hashable(&unhashable_value) {
    map.insert(unhashable_value, data);
} else {
    map.insert(repr(&unhashable_value), data);
}
Defensive patterns

Strategy: validation

Validate before calling

fn is_hashable(v: &MontyObject) -> bool {
    use MontyObject::*;
    !matches!(v, List(_) | Dict(_) | Set(_) | Bytearray(_) | Cycle(_, _))
}

Type guard

fn is_hashable(v: &MontyObject) -> bool {
    use MontyObject::*;
    matches!(v, None | Bool(_) | Int(_) | Float(_) | Str(_) | Bytes(_) | Tuple(_) | FrozenSet(_) | FileHandle(_) | Type(_))
}

Try / catch

// panics, not Result — check hashability first
if is_hashable(&v) { map.insert(v, data); } else { map.insert(repr(&v), data); }

Prevention

When it happens

Trigger: Calling hash()/hash_of on a MontyObject whose variant is an unhashable container or object — e.g. using a list or dict as a dict key or in a set in host-side code that invokes this hash implementation.

Common situations: Host code using MontyObject values as HashMap keys without filtering; converting host-side sets keyed by returned sandbox values; a missing match arm for a newly added type landing in the `_` catch-all.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13). Data as JSON: /api/errors/81126dbbd886c464. Report an issue: GitHub.