diff --git a/lib/galaxy/tools/parameters/basic.py b/lib/galaxy/tools/parameters/basic.py index a60053b711d..65d2a7c8767 100644 --- a/lib/galaxy/tools/parameters/basic.py +++ b/lib/galaxy/tools/parameters/basic.py @@ -2275,7 +2275,23 @@ class DataToolParameter(BaseDataToolParameter): if len(rval) > 1: raise ParameterValueError("more than one dataset supplied to single input dataset parameter", self.name) if len(rval) > 0: - return rval[0] + single_value = rval[0] + if isinstance(single_value, HistoryDatasetCollectionAssociation): + # A non-multiple data parameter cannot reduce a dataset + # collection. Without this check the HDCA is silently + # accepted here, then ``collect_input_dataset_collections`` + # rewrites the state to a list of the collection's HDAs and + # ``wrap_values`` later crashes with a raw ``TypeError`` + # (https://github.com/galaxyproject/galaxy/issues/22401). + # Map-over remains supported via ``{"batch": true, ...}``, + # which is expanded to per-element jobs before reaching + # ``from_json``. + raise ParameterValueError( + "dataset collection supplied to single input dataset parameter; " + "to run the tool over each element of the collection, use the map-over option", + self.name, + ) + return single_value else: raise ParameterValueError("invalid dataset supplied to single input dataset parameter", self.name) return rval diff --git a/lib/galaxy_test/api/test_tools.py b/lib/galaxy_test/api/test_tools.py index 1f661ffcbb5..5c69b534585 100644 --- a/lib/galaxy_test/api/test_tools.py +++ b/lib/galaxy_test/api/test_tools.py @@ -2510,6 +2510,22 @@ class TestToolsApi(ApiTestCase, TestsTools): collection.assert_has_dataset_element("forward").with_contents_stripped("forward") collection.assert_has_dataset_element("reverse").with_contents_stripped("reverse") + @skip_without_tool("identifier_in_conditional") + def test_hdca_rejected_for_single_data_param_in_conditional(self, history_id): + # Regression test for https://github.com/galaxyproject/galaxy/issues/22401 . + # Submitting a paired collection (no batch wrapper) to a non-multiple + # ``data`` parameter is invalid and must produce a 400 client error, + # not a 500 from a TypeError raised inside ``wrap_values``. Map-over is + # still supported via ``{"batch": True, "values": [...]}``, exercised + # by ``test_identifier_map_over_input_in_conditional``. + hdca_id = self._build_pair(history_id, ["123", "456"]) + inputs = { + "outer_cond|multi_input": False, + "outer_cond|input1": {"src": "hdca", "id": hdca_id}, + } + response = self._run("identifier_in_conditional", history_id, inputs) + self._assert_status_code_is(response, 400) + @skip_without_tool("identifier_multiple_in_conditional") def test_identifier_multiple_reduce_in_conditional(self, history_id): hdca_id = self._build_pair(history_id, ["123", "456"])