{"record":{"id":"c2c5fbcc8f19fa5b","repo":"pola-rs/polars","slug":"should-be-hashable","errorCode":null,"errorMessage":"should be hashable","messagePattern":"should be hashable","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/polars-python/src/conversion/mod.rs","lineNumber":753,"sourceCode":"}\n\n#[derive(Debug)]\n#[repr(transparent)]\npub struct ObjectValue {\n    pub inner: Py<PyAny>,\n}\n\nimpl Clone for ObjectValue {\n    fn clone(&self) -> Self {\n        Python::attach(|py| Self {\n            inner: self.inner.clone_ref(py),\n        })\n    }\n}\n\nimpl Hash for ObjectValue {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        let h = Python::attach(|py| self.inner.bind(py).hash().expect(\"should be hashable\"));\n        state.write_isize(h)\n    }\n}\n\nimpl Eq for ObjectValue {}\n\nimpl PartialEq for ObjectValue {\n    fn eq(&self, other: &Self) -> bool {\n        Python::attach(|py| {\n            match self\n                .inner\n                .bind(py)\n                .rich_compare(other.inner.bind(py), CompareOp::Eq)\n            {\n                Ok(result) => result.is_truthy().unwrap(),\n                Err(_) => false,\n            }\n        })","sourceCodeStart":735,"sourceCodeEnd":771,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/crates/polars-python/src/conversion/mod.rs#L735-L771","documentation":"ObjectValue wraps an arbitrary Python object stored in an Object-dtype column. Its Hash implementation calls Python's hash() on the wrapped value; if that value is unhashable (dict, list, set — anything with __hash__ = None), the Python call fails and this expect panics with 'should be hashable'.","triggerScenarios":"Any hash-based operation on an Object column containing unhashable Python values: group_by, unique/n_unique, joins on that column, is_duplicated, value_counts.","commonSituations":"Columns built from Python dicts/lists/objects (e.g. via apply returning complex values) then used as group-by or join keys; user classes that define __eq__ without __hash__ (Python then sets __hash__ to None).","solutions":["Cast the Object column to String (e.g. via map to repr/json) before group_by/unique/join","If using custom classes, define both __eq__ and __hash__ consistently","Avoid storing unhashable values in Object columns destined for key-based operations","Use a struct column instead of objects when the data is record-shaped"],"exampleFix":"# before\n(df.group_by(pl.col(\"obj\")).agg(pl.len()))  # obj holds dicts -> panic 'should be hashable'\n\n# after\ndf.group_by(pl.col(\"obj\").map_elements(lambda v: repr(v), return_dtype=pl.String)).agg(pl.len())\n\n# or on the class side\nclass Key:\n    def __init__(self, v): self.v = v\n    def __eq__(self, o): return isinstance(o, Key) and o.v == self.v\n    def __hash__(self): return hash(self.v)","handlingStrategy":"type-guard","validationCode":"# Python: verify object column is hashable before hash-based ops\ndef column_is_hashable(s: pl.Series) -> bool:\n    return all(is_hashable(v) for v in s.head(100).to_list())\n\nfrom collections.abc import Hashable\ndef is_hashable(v) -> bool:\n    try:\n        hash(v)\n        return True\n    except TypeError:\n        return False","typeGuard":"def is_hashable(v) -> bool:\n    try:\n        hash(v); return True\n    except TypeError:\n        return False","tryCatchPattern":null,"preventionTips":["Avoid Object dtype for group-by/join keys; use String or Struct","Define __hash__ whenever you define __eq__ on stored classes","Convert unhashable values to a canonical string form up front"],"tags":["python","object-dtype","hash","group-by","panic","ffi"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}