diff --git a/lib/galaxy/tools/parameters/basic.py b/lib/galaxy/tools/parameters/basic.py
index 05cbb2c6b8d..ef01c27f7f6 100644
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -1117,6 +1117,17 @@ class SelectToolParameter(ToolParameter):
)
if is_runtime_value(value):
return None
+ if isinstance(value, dict):
+ # A dict is unhashable and can never be a legal value, but
+ # testing membership against the set of legal values would
+ # raise an opaque "unhashable type" TypeError. Treat it as an
+ # invalid option instead.
+ raise ParameterValueError(
+ f"an invalid option ({value!r}) was selected (valid options: {','.join(iter_to_string(legal_values))})",
+ self.name,
+ value,
+ is_dynamic=self.is_dynamic,
+ )
if value in legal_values:
return value
elif value in fallback_values:
diff --git a/test/unit/app/tools/test_select_parameters.py b/test/unit/app/tools/test_select_parameters.py
index 6c9fcaa38f1..b24ba677211 100644
--- a/test/unit/app/tools/test_select_parameters.py
+++ b/test/unit/app/tools/test_select_parameters.py
@@ -26,6 +26,14 @@ class TestSelectToolParameter(BaseParameterTestCase):
self.param.from_json("42", self.trans)
assert str(exc_info.value) == "Parameter 'my_name': requires a value, but no legal values defined"
+ def test_dict_value_is_invalid_option(self):
+ # A dict value is unhashable; checking it against the set of legal
+ # values used to raise an opaque "unhashable type: 'dict'" TypeError.
+ param = self._parameter_for(xml="""""")
+ with pytest.raises(ValueError) as exc_info:
+ param.from_json({"not": "valid"}, self.trans)
+ assert "an invalid option" in str(exc_info.value)
+
def test_unvalidated_values(self):
self.options_xml = """"""
self.trans.workflow_building_mode = True