You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by el...@apache.org on 2026/09/20 15:11:59 UTC

(superset) 01/01: fix(mcp): catch ValueError for reversed date range in query_dataset

This is an automated email from the ASF dual-hosted git repository.

eschutho pushed a commit to branch fix-mcp-query-dataset-reversed-date-range
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 3d119c5d1058306e6dfdafc02af878dac4ecadfe
Author: Elizabeth Thompson <es...@gmail.com>
AuthorDate: Sun Sep 20 15:11:44 2026 +0000

    fix(mcp): catch ValueError for reversed date range in query_dataset
---
 superset/mcp_service/dataset/tool/query_dataset.py | 11 +++++
 .../mcp_service/dataset/tool/test_query_dataset.py | 52 ++++++++++++++++++++++
 2 files changed, 63 insertions(+)

diff --git a/superset/mcp_service/dataset/tool/query_dataset.py b/superset/mcp_service/dataset/tool/query_dataset.py
index f49dc158605..9cd12bfabaf 100644
--- a/superset/mcp_service/dataset/tool/query_dataset.py
+++ b/superset/mcp_service/dataset/tool/query_dataset.py
@@ -433,6 +433,17 @@ async def query_dataset(  # noqa: C901
             error_type="DatabaseError",
         )
 
+    except ValueError as exc:
+        # Expected user/agent-input validation (e.g. a reversed time_range where
+        # since > until raises "From date cannot be larger than to date" from
+        # date_parser.get_since_until). Return an actionable ValidationError
+        # without a full-traceback log — this is not a bug.
+        await ctx.error("Invalid request: %s" % (str(exc),))
+        return DatasetError.create(
+            error=str(exc),
+            error_type="ValidationError",
+        )
+
     except Exception as exc:
         logger.exception(
             "Unexpected error while querying dataset: %s: %s",
diff --git a/tests/unit_tests/mcp_service/dataset/tool/test_query_dataset.py b/tests/unit_tests/mcp_service/dataset/tool/test_query_dataset.py
index a4513a0a671..a055f08a4e0 100644
--- a/tests/unit_tests/mcp_service/dataset/tool/test_query_dataset.py
+++ b/tests/unit_tests/mcp_service/dataset/tool/test_query_dataset.py
@@ -435,6 +435,58 @@ async def test_query_dataset_time_range_no_temporal_column(mcp_server: FastMCP)
     assert "temporal column" in data["error"].lower()
 
 
+@pytest.mark.asyncio
+async def test_query_dataset_reversed_time_range(mcp_server: FastMCP) -> None:
+    """A reversed explicit time_range (since > until) resolves to a clean
+    ValidationError rather than a generic UnexpectedError.
+
+    An explicit ``"<start> : <end>"`` range where start > end passes the
+    pydantic-level validate_time_range guard unchanged (by design) and reaches
+    the real get_since_until(), which raises
+    ``ValueError("From date cannot be larger than to date")``. The tool must
+    surface that as an actionable ValidationError, not swallow it into the
+    catch-all UnexpectedError arm.
+    """
+    dataset = _make_dataset(main_dttm_col="order_date")
+    reversed_range = "2024-01-01T00:00:00 : 2020-01-01T00:00:00"
+
+    def create_with_real_parser(**kwargs):
+        # Exercise the real parser on the reversed range exactly as the query
+        # pipeline would, letting its genuine ValueError propagate.
+        for query in kwargs.get("queries", []):
+            for filt in query.get("filters", []):
+                if filt.get("op") == "TEMPORAL_RANGE":
+                    get_since_until(time_range=filt["val"])
+        return MagicMock()
+
+    with (
+        patch.object(
+            query_dataset_module,
+            "resolve_dataset",
+            return_value=dataset,
+        ),
+        patch(
+            "superset.common.query_context_factory.QueryContextFactory.create",
+            side_effect=create_with_real_parser,
+        ),
+    ):
+        async with Client(mcp_server) as client:
+            result = await client.call_tool(
+                "query_dataset",
+                {
+                    "request": {
+                        "dataset_id": 1,
+                        "metrics": ["count"],
+                        "time_range": reversed_range,
+                    }
+                },
+            )
+
+    data = json.loads(result.content[0].text)
+    assert data["error_type"] == "ValidationError"
+    assert "From date cannot be larger than to date" in data["error"]
+
+
 @pytest.mark.asyncio
 async def test_query_dataset_with_filters(mcp_server: FastMCP) -> None:
     """User-provided filters are passed through to the query."""