This is an automated email from the ASF dual-hosted git repository.
aminghadersohi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 5dbc8009504 fix(semantic-layer): use fixed messages for selected
metadata failures (#45531)
5dbc8009504 is described below
commit 5dbc8009504d5ff5c865a808b6559b4074559f63
Author: Mike Bridge <[email protected]>
AuthorDate: Mon Oct 26 11:38:05 2026 -0600
fix(semantic-layer): use fixed messages for selected metadata failures
(#45531)
---
.../tool/get_compatible_dimensions.py | 8 +-
.../semantic_layer/tool/get_compatible_metrics.py | 8 +-
.../semantic_layer/tool/list_metrics.py | 17 ++-
superset/semantic_layers/api.py | 21 ++--
superset/translations/ar/LC_MESSAGES/messages.po | 6 +
superset/translations/ca/LC_MESSAGES/messages.po | 6 +
superset/translations/cs/LC_MESSAGES/messages.po | 6 +
superset/translations/de/LC_MESSAGES/messages.po | 6 +
superset/translations/en/LC_MESSAGES/messages.po | 6 +
superset/translations/es/LC_MESSAGES/messages.po | 6 +
superset/translations/fa/LC_MESSAGES/messages.po | 6 +
superset/translations/fi/LC_MESSAGES/messages.po | 6 +
superset/translations/fr/LC_MESSAGES/messages.po | 6 +
superset/translations/it/LC_MESSAGES/messages.po | 6 +
superset/translations/ja/LC_MESSAGES/messages.po | 6 +
superset/translations/ko/LC_MESSAGES/messages.po | 6 +
superset/translations/lv/LC_MESSAGES/messages.po | 6 +
superset/translations/messages.pot | 6 +
superset/translations/mi/LC_MESSAGES/messages.po | 6 +
superset/translations/nl/LC_MESSAGES/messages.po | 6 +
superset/translations/pl/LC_MESSAGES/messages.po | 6 +
superset/translations/pt/LC_MESSAGES/messages.po | 6 +
.../translations/pt_BR/LC_MESSAGES/messages.po | 6 +
superset/translations/ro/LC_MESSAGES/messages.po | 6 +
superset/translations/ru/LC_MESSAGES/messages.po | 6 +
superset/translations/sk/LC_MESSAGES/messages.po | 6 +
superset/translations/sl/LC_MESSAGES/messages.po | 6 +
superset/translations/sr/LC_MESSAGES/messages.po | 6 +
.../translations/sr_Latn/LC_MESSAGES/messages.po | 6 +
superset/translations/ta/LC_MESSAGES/messages.po | 6 +
superset/translations/th/LC_MESSAGES/messages.po | 6 +
superset/translations/tr/LC_MESSAGES/messages.po | 6 +
superset/translations/uk/LC_MESSAGES/messages.po | 6 +
superset/translations/zh/LC_MESSAGES/messages.po | 6 +
.../translations/zh_TW/LC_MESSAGES/messages.po | 6 +
.../tool/test_compatibility_errors.py | 133 +++++++++++++++++++++
.../semantic_layer/tool/test_list_metrics.py | 105 +++++++++++++++-
tests/unit_tests/semantic_layers/api_test.py | 42 ++++---
38 files changed, 484 insertions(+), 36 deletions(-)
diff --git
a/superset/mcp_service/semantic_layer/tool/get_compatible_dimensions.py
b/superset/mcp_service/semantic_layer/tool/get_compatible_dimensions.py
index 590cdf6a64a..b253fe8d938 100644
--- a/superset/mcp_service/semantic_layer/tool/get_compatible_dimensions.py
+++ b/superset/mcp_service/semantic_layer/tool/get_compatible_dimensions.py
@@ -45,6 +45,7 @@ from superset.semantic_layers.access import (
is_semantic_layers_enabled,
SemanticLayersDisabledError,
)
+from superset.semantic_layers.dimension_resolution import
AmbiguousDimensionError
logger = logging.getLogger(__name__)
@@ -270,14 +271,17 @@ async def _resolve_compatible_dimensions(
source="external",
)
+ except AmbiguousDimensionError as exc:
+ return SemanticLayerError.create(error=str(exc),
error_type="ValidationError")
except Exception as exc:
logger.exception(
"Unexpected error in get_compatible_dimensions: %s: %s",
type(exc).__name__,
str(exc),
)
- await ctx.error("Unexpected error: %s: %s" % (type(exc).__name__,
str(exc)))
+ message: str = "Unable to retrieve compatible dimensions."
+ await ctx.error(message)
return SemanticLayerError.create(
- error=f"Internal error in get_compatible_dimensions: {exc}",
+ error=message,
error_type="InternalError",
)
diff --git a/superset/mcp_service/semantic_layer/tool/get_compatible_metrics.py
b/superset/mcp_service/semantic_layer/tool/get_compatible_metrics.py
index 66ec50aad61..b594d9767f6 100644
--- a/superset/mcp_service/semantic_layer/tool/get_compatible_metrics.py
+++ b/superset/mcp_service/semantic_layer/tool/get_compatible_metrics.py
@@ -45,6 +45,7 @@ from superset.semantic_layers.access import (
is_semantic_layers_enabled,
SemanticLayersDisabledError,
)
+from superset.semantic_layers.dimension_resolution import
AmbiguousDimensionError
logger = logging.getLogger(__name__)
@@ -277,14 +278,17 @@ async def _resolve_compatible_metrics(
source="external",
)
+ except AmbiguousDimensionError as exc:
+ return SemanticLayerError.create(error=str(exc),
error_type="ValidationError")
except Exception as exc:
logger.exception(
"Unexpected error in get_compatible_metrics: %s: %s",
type(exc).__name__,
str(exc),
)
- await ctx.error("Unexpected error: %s: %s" % (type(exc).__name__,
str(exc)))
+ message: str = "Unable to retrieve compatible metrics."
+ await ctx.error(message)
return SemanticLayerError.create(
- error=f"Internal error in get_compatible_metrics: {exc}",
+ error=message,
error_type="InternalError",
)
diff --git a/superset/mcp_service/semantic_layer/tool/list_metrics.py
b/superset/mcp_service/semantic_layer/tool/list_metrics.py
index 28b0588ab2c..8844682b5ee 100644
--- a/superset/mcp_service/semantic_layer/tool/list_metrics.py
+++ b/superset/mcp_service/semantic_layer/tool/list_metrics.py
@@ -47,6 +47,7 @@ from superset.semantic_layers.access import (
is_semantic_layers_enabled,
SemanticLayersDisabledError,
)
+from superset.semantic_layers.dimension_resolution import
AmbiguousDimensionError
from superset.semantic_layers.models import ColumnMetadata, SemanticView
logger = logging.getLogger(__name__)
@@ -228,11 +229,18 @@ async def _collect_external_metrics(
compatible_dimensions=compat_dims,
)
)
- except Exception as exc: # noqa: BLE001
- # External registry may be empty in OSS — degrade gracefully
+ except AmbiguousDimensionError as exc:
await ctx.warning(
"Could not load metrics for view id=%s: %s" % (view.id,
str(exc))
)
+ except Exception: # noqa: BLE001
+ # External registry may be empty in OSS — degrade gracefully
+ logger.warning(
+ "Could not load metrics for view id=%s", view.id, exc_info=True
+ )
+ await ctx.warning(
+ "Unable to retrieve metrics for semantic view id=%s." % view.id
+ )
return results
@@ -347,8 +355,9 @@ async def list_metrics(
logger.exception(
"Unexpected error in list_metrics: %s: %s", type(exc).__name__,
str(exc)
)
- await ctx.error("Unexpected error: %s: %s" % (type(exc).__name__,
str(exc)))
+ message: str = "Unable to retrieve metrics."
+ await ctx.error(message)
return SemanticLayerError.create(
- error=f"Internal error listing metrics: {exc}",
+ error=message,
error_type="InternalError",
)
diff --git a/superset/semantic_layers/api.py b/superset/semantic_layers/api.py
index 165bbf4126d..ed21f137618 100644
--- a/superset/semantic_layers/api.py
+++ b/superset/semantic_layers/api.py
@@ -338,14 +338,14 @@ class SemanticViewRestApi(_RuntimeGuardedApiMixin,
BaseSupersetModelRestApi):
]
except SupersetSecurityException as ex:
return self.response(403, message=ex.message,
errors=[ex.to_dict()])
- except Exception as ex: # pylint: disable=broad-except
- logger.error(
- "Error fetching structure for semantic view %d: %s",
+ except Exception: # pylint: disable=broad-except
+ logger.exception(
+ "Error fetching structure for semantic view %d",
pk,
- str(ex),
- exc_info=True,
)
- return self.response_422(message=str(ex))
+ return self.response_422(
+ message=t("Unable to fetch semantic view structure.")
+ )
return self.response(
200,
@@ -903,8 +903,13 @@ class SemanticLayerRestApi(_RuntimeGuardedApiMixin,
BaseSupersetApi):
)
except SupersetSecurityException as ex:
return self.response(403, message=ex.message,
errors=[ex.to_dict()])
- except Exception as ex: # pylint: disable=broad-except
- return self.response_400(message=str(ex))
+ except Exception: # pylint: disable=broad-except
+ logger.exception(
+ "Error fetching runtime schema for semantic layer %s", uuid
+ )
+ return self.response_400(
+ message=t("Unable to fetch semantic layer runtime schema.")
+ )
return self.response(200, result=schema)
diff --git a/superset/translations/ar/LC_MESSAGES/messages.po
b/superset/translations/ar/LC_MESSAGES/messages.po
index 7ac20dfe32e..d8802b51649 100644
--- a/superset/translations/ar/LC_MESSAGES/messages.po
+++ b/superset/translations/ar/LC_MESSAGES/messages.po
@@ -20115,6 +20115,12 @@ msgstr "غير قادر على فك شفرة القيمة"
msgid "Unable to encode value"
msgstr "غير قادر على ترميز القيمة"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/ca/LC_MESSAGES/messages.po
b/superset/translations/ca/LC_MESSAGES/messages.po
index a3e9118225e..7ab8f9b1ef6 100644
--- a/superset/translations/ca/LC_MESSAGES/messages.po
+++ b/superset/translations/ca/LC_MESSAGES/messages.po
@@ -19536,6 +19536,12 @@ msgstr "No es pot descodificar valor"
msgid "Unable to encode value"
msgstr "No es pot codificar valor"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/cs/LC_MESSAGES/messages.po
b/superset/translations/cs/LC_MESSAGES/messages.po
index 1b91cbba880..ea32365af5a 100644
--- a/superset/translations/cs/LC_MESSAGES/messages.po
+++ b/superset/translations/cs/LC_MESSAGES/messages.po
@@ -18872,6 +18872,12 @@ msgstr "Nelze dekódovat hodnotu"
msgid "Unable to encode value"
msgstr "Nelze kódovat hodnotu"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/de/LC_MESSAGES/messages.po
b/superset/translations/de/LC_MESSAGES/messages.po
index 2869a850635..ec887babaef 100644
--- a/superset/translations/de/LC_MESSAGES/messages.po
+++ b/superset/translations/de/LC_MESSAGES/messages.po
@@ -18491,6 +18491,12 @@ msgstr "Wert kann nicht dekodiert werden"
msgid "Unable to encode value"
msgstr "Wert kann nicht kodiert werden"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
msgid "Unable to fetch semantic views. Check the layer configuration."
msgstr ""
"Semantische Ansichten können nicht abgerufen werden. Überprüfen Sie die "
diff --git a/superset/translations/en/LC_MESSAGES/messages.po
b/superset/translations/en/LC_MESSAGES/messages.po
index 9174659d6d5..160cc77d7e3 100644
--- a/superset/translations/en/LC_MESSAGES/messages.po
+++ b/superset/translations/en/LC_MESSAGES/messages.po
@@ -17020,6 +17020,12 @@ msgstr ""
msgid "Unable to encode value"
msgstr ""
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
msgid "Unable to fetch semantic views. Check the layer configuration."
msgstr ""
diff --git a/superset/translations/es/LC_MESSAGES/messages.po
b/superset/translations/es/LC_MESSAGES/messages.po
index faf7b8afc04..b79123d0b13 100644
--- a/superset/translations/es/LC_MESSAGES/messages.po
+++ b/superset/translations/es/LC_MESSAGES/messages.po
@@ -19418,6 +19418,12 @@ msgstr "No se ha podido decodificar el valor"
msgid "Unable to encode value"
msgstr "No se ha podido codificar el valor"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/fa/LC_MESSAGES/messages.po
b/superset/translations/fa/LC_MESSAGES/messages.po
index 5eec0ea9bde..446bdd5622e 100644
--- a/superset/translations/fa/LC_MESSAGES/messages.po
+++ b/superset/translations/fa/LC_MESSAGES/messages.po
@@ -19640,6 +19640,12 @@ msgstr "قادر به رمزگشایی مقدار نیست"
msgid "Unable to encode value"
msgstr "نمیتوان مقدار را کدگذاری کرد"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/fi/LC_MESSAGES/messages.po
b/superset/translations/fi/LC_MESSAGES/messages.po
index 1b316d8a66b..66880baca63 100644
--- a/superset/translations/fi/LC_MESSAGES/messages.po
+++ b/superset/translations/fi/LC_MESSAGES/messages.po
@@ -30202,6 +30202,12 @@ msgstr "Arvon purkaminen epäonnistui"
msgid "Unable to encode value"
msgstr "Arvon koodaaminen epäonnistui"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/fr/LC_MESSAGES/messages.po
b/superset/translations/fr/LC_MESSAGES/messages.po
index 501302f8cc9..17d3b05f7b4 100644
--- a/superset/translations/fr/LC_MESSAGES/messages.po
+++ b/superset/translations/fr/LC_MESSAGES/messages.po
@@ -18832,6 +18832,12 @@ msgstr "Impossible de décoder la valeur"
msgid "Unable to encode value"
msgstr "Impossible de coder la valeur"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
msgid "Unable to fetch semantic views. Check the layer configuration."
diff --git a/superset/translations/it/LC_MESSAGES/messages.po
b/superset/translations/it/LC_MESSAGES/messages.po
index 4aa7c7f8a42..49c495b53c5 100644
--- a/superset/translations/it/LC_MESSAGES/messages.po
+++ b/superset/translations/it/LC_MESSAGES/messages.po
@@ -23122,6 +23122,12 @@ msgstr "Impossibile decodificare il valore"
msgid "Unable to encode value"
msgstr "Impossibile codificare il valore"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
msgid "Unable to fetch semantic views. Check the layer configuration."
diff --git a/superset/translations/ja/LC_MESSAGES/messages.po
b/superset/translations/ja/LC_MESSAGES/messages.po
index 7fb69354c31..216b37fd742 100644
--- a/superset/translations/ja/LC_MESSAGES/messages.po
+++ b/superset/translations/ja/LC_MESSAGES/messages.po
@@ -17109,6 +17109,12 @@ msgstr "値をデコードできません"
msgid "Unable to encode value"
msgstr "値をエンコードできません"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
msgid "Unable to fetch semantic views. Check the layer configuration."
msgstr "セマンティックビューを取得できません。レイヤーの設定を確認してください。"
diff --git a/superset/translations/ko/LC_MESSAGES/messages.po
b/superset/translations/ko/LC_MESSAGES/messages.po
index 8c1c0bd639c..59f9d4bc5e9 100644
--- a/superset/translations/ko/LC_MESSAGES/messages.po
+++ b/superset/translations/ko/LC_MESSAGES/messages.po
@@ -27412,6 +27412,12 @@ msgstr "값을 디코딩할 수 없습니다"
msgid "Unable to encode value"
msgstr "값을 인코딩할 수 없습니다"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/lv/LC_MESSAGES/messages.po
b/superset/translations/lv/LC_MESSAGES/messages.po
index ac5a3afa85c..b9bab39af35 100644
--- a/superset/translations/lv/LC_MESSAGES/messages.po
+++ b/superset/translations/lv/LC_MESSAGES/messages.po
@@ -18531,6 +18531,12 @@ msgstr "Nevar dekodēt vērtību"
msgid "Unable to encode value"
msgstr "Nevar kodēt vērtību"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/messages.pot
b/superset/translations/messages.pot
index ba23d91efe7..0e31468ad1f 100644
--- a/superset/translations/messages.pot
+++ b/superset/translations/messages.pot
@@ -15746,6 +15746,12 @@ msgstr ""
msgid "Unable to encode value"
msgstr ""
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
msgid "Unable to fetch semantic views. Check the layer configuration."
msgstr ""
diff --git a/superset/translations/mi/LC_MESSAGES/messages.po
b/superset/translations/mi/LC_MESSAGES/messages.po
index 747f9223906..44fe489c5d9 100644
--- a/superset/translations/mi/LC_MESSAGES/messages.po
+++ b/superset/translations/mi/LC_MESSAGES/messages.po
@@ -19435,6 +19435,12 @@ msgstr "Kāore e taea te whakaoti i te uara"
msgid "Unable to encode value"
msgstr "Kāore e taea te whakakohu i te uara"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/nl/LC_MESSAGES/messages.po
b/superset/translations/nl/LC_MESSAGES/messages.po
index fc4ff05ef6c..b10a0067662 100644
--- a/superset/translations/nl/LC_MESSAGES/messages.po
+++ b/superset/translations/nl/LC_MESSAGES/messages.po
@@ -20096,6 +20096,12 @@ msgstr "Kan waarde niet decoderen"
msgid "Unable to encode value"
msgstr "Kan waarde niet coderen"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/pl/LC_MESSAGES/messages.po
b/superset/translations/pl/LC_MESSAGES/messages.po
index 1ba185cb900..cfb9d19a73e 100644
--- a/superset/translations/pl/LC_MESSAGES/messages.po
+++ b/superset/translations/pl/LC_MESSAGES/messages.po
@@ -20430,6 +20430,12 @@ msgstr "Nie można zdekodować wartości"
msgid "Unable to encode value"
msgstr "Nie można zakodować wartości"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/pt/LC_MESSAGES/messages.po
b/superset/translations/pt/LC_MESSAGES/messages.po
index 7e46e8c53fc..659118e802c 100644
--- a/superset/translations/pt/LC_MESSAGES/messages.po
+++ b/superset/translations/pt/LC_MESSAGES/messages.po
@@ -23832,6 +23832,12 @@ msgstr "Não foi possível decodificar o valor"
msgid "Unable to encode value"
msgstr "Não foi possível codificar o valor"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
msgid "Unable to fetch semantic views. Check the layer configuration."
diff --git a/superset/translations/pt_BR/LC_MESSAGES/messages.po
b/superset/translations/pt_BR/LC_MESSAGES/messages.po
index 4c6b8c30a0b..fe7b3c8201b 100644
--- a/superset/translations/pt_BR/LC_MESSAGES/messages.po
+++ b/superset/translations/pt_BR/LC_MESSAGES/messages.po
@@ -20200,6 +20200,12 @@ msgstr "Não foi possível decodificar o valor"
msgid "Unable to encode value"
msgstr "Não foi possível codificar o valor"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/ro/LC_MESSAGES/messages.po
b/superset/translations/ro/LC_MESSAGES/messages.po
index 4b459e00418..1a187475a25 100644
--- a/superset/translations/ro/LC_MESSAGES/messages.po
+++ b/superset/translations/ro/LC_MESSAGES/messages.po
@@ -19736,6 +19736,12 @@ msgstr "Nu se poate decodifica valoarea"
msgid "Unable to encode value"
msgstr "Nu se poate codifica valoarea"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, ja, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/ru/LC_MESSAGES/messages.po
b/superset/translations/ru/LC_MESSAGES/messages.po
index 112175f06f6..aefd63c870b 100644
--- a/superset/translations/ru/LC_MESSAGES/messages.po
+++ b/superset/translations/ru/LC_MESSAGES/messages.po
@@ -18216,6 +18216,12 @@ msgstr "Не удалось декодировать значение"
msgid "Unable to encode value"
msgstr "Не удалось закодировать значение"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
msgid "Unable to fetch semantic views. Check the layer configuration."
msgstr "Не удалось получить представления аннотаций. Проверьте конфигурацию
слоя."
diff --git a/superset/translations/sk/LC_MESSAGES/messages.po
b/superset/translations/sk/LC_MESSAGES/messages.po
index 0da4871a929..c2ea6426cfb 100644
--- a/superset/translations/sk/LC_MESSAGES/messages.po
+++ b/superset/translations/sk/LC_MESSAGES/messages.po
@@ -18882,6 +18882,12 @@ msgstr "Nemožno dekódovať hodnotu"
msgid "Unable to encode value"
msgstr "Nemožno kódovať hodnotu"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/sl/LC_MESSAGES/messages.po
b/superset/translations/sl/LC_MESSAGES/messages.po
index 208c3ecd625..c4442564f8e 100644
--- a/superset/translations/sl/LC_MESSAGES/messages.po
+++ b/superset/translations/sl/LC_MESSAGES/messages.po
@@ -18744,6 +18744,12 @@ msgstr "Vrednosti ni mogoče dešifrirati"
msgid "Unable to encode value"
msgstr "Vrednosti ni mogoče šifrirati"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
msgid "Unable to fetch semantic views. Check the layer configuration."
diff --git a/superset/translations/sr/LC_MESSAGES/messages.po
b/superset/translations/sr/LC_MESSAGES/messages.po
index dcd577e6ae9..45298599e33 100644
--- a/superset/translations/sr/LC_MESSAGES/messages.po
+++ b/superset/translations/sr/LC_MESSAGES/messages.po
@@ -22215,6 +22215,12 @@ msgstr "Није могуће декодирати вредност"
msgid "Unable to encode value"
msgstr "Није могуће кодирати вредност"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
msgid "Unable to fetch semantic views. Check the layer configuration."
msgstr "Није могуће преузети семантичке приказе. Проверите конфигурацију
слоја."
diff --git a/superset/translations/sr_Latn/LC_MESSAGES/messages.po
b/superset/translations/sr_Latn/LC_MESSAGES/messages.po
index da83493c008..822c1b1dc06 100644
--- a/superset/translations/sr_Latn/LC_MESSAGES/messages.po
+++ b/superset/translations/sr_Latn/LC_MESSAGES/messages.po
@@ -22232,6 +22232,12 @@ msgstr "Nije moguće dekodirati vrednost"
msgid "Unable to encode value"
msgstr "Nije moguće kodirati vrednost"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
msgid "Unable to fetch semantic views. Check the layer configuration."
msgstr "Nije moguće preuzeti semantičke prikaze. Proverite konfiguraciju
sloja."
diff --git a/superset/translations/ta/LC_MESSAGES/messages.po
b/superset/translations/ta/LC_MESSAGES/messages.po
index 7d202d85851..647e1e8fdda 100644
--- a/superset/translations/ta/LC_MESSAGES/messages.po
+++ b/superset/translations/ta/LC_MESSAGES/messages.po
@@ -18000,6 +18000,12 @@ msgstr "மதிப்பை டிகோட் செய்ய முடிய
msgid "Unable to encode value"
msgstr "மதிப்பை என்கோட் செய்ய முடியவில்லை"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
msgid "Unable to fetch semantic views. Check the layer configuration."
msgstr ""
diff --git a/superset/translations/th/LC_MESSAGES/messages.po
b/superset/translations/th/LC_MESSAGES/messages.po
index 403ad478e73..1067d544fba 100644
--- a/superset/translations/th/LC_MESSAGES/messages.po
+++ b/superset/translations/th/LC_MESSAGES/messages.po
@@ -29854,6 +29854,12 @@ msgstr "ไม่สามารถถอดรหัสค่าได้"
msgid "Unable to encode value"
msgstr "ไม่สามารถเข้ารหัสค่าได้"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/tr/LC_MESSAGES/messages.po
b/superset/translations/tr/LC_MESSAGES/messages.po
index 53edce14b60..58bdfebc9e0 100644
--- a/superset/translations/tr/LC_MESSAGES/messages.po
+++ b/superset/translations/tr/LC_MESSAGES/messages.po
@@ -19145,6 +19145,12 @@ msgstr "Decode değer"
msgid "Unable to encode value"
msgstr "kodlama değeri"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/uk/LC_MESSAGES/messages.po
b/superset/translations/uk/LC_MESSAGES/messages.po
index a569732ac3e..e3fdd6da66b 100644
--- a/superset/translations/uk/LC_MESSAGES/messages.po
+++ b/superset/translations/uk/LC_MESSAGES/messages.po
@@ -18540,6 +18540,12 @@ msgstr "Не вдалося декодувати значення"
msgid "Unable to encode value"
msgstr "Неможливо закодувати значення"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/zh/LC_MESSAGES/messages.po
b/superset/translations/zh/LC_MESSAGES/messages.po
index 1e04452884f..d4027b8fd46 100644
--- a/superset/translations/zh/LC_MESSAGES/messages.po
+++ b/superset/translations/zh/LC_MESSAGES/messages.po
@@ -19004,6 +19004,12 @@ msgstr "无法解码值"
msgid "Unable to encode value"
msgstr "无法编码值"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git a/superset/translations/zh_TW/LC_MESSAGES/messages.po
b/superset/translations/zh_TW/LC_MESSAGES/messages.po
index 13c739030f0..847b56bf81f 100644
--- a/superset/translations/zh_TW/LC_MESSAGES/messages.po
+++ b/superset/translations/zh_TW/LC_MESSAGES/messages.po
@@ -20235,6 +20235,12 @@ msgstr "無法解碼值"
msgid "Unable to encode value"
msgstr "無法編碼值"
+msgid "Unable to fetch semantic layer runtime schema."
+msgstr ""
+
+msgid "Unable to fetch semantic view structure."
+msgstr ""
+
# Machine-translated via backfill_po.py (claude-sonnet-4-6) [refs: de, sr,
# sr_Latn]
#, fuzzy
diff --git
a/tests/unit_tests/mcp_service/semantic_layer/tool/test_compatibility_errors.py
b/tests/unit_tests/mcp_service/semantic_layer/tool/test_compatibility_errors.py
new file mode 100644
index 00000000000..150ee746411
--- /dev/null
+++
b/tests/unit_tests/mcp_service/semantic_layer/tool/test_compatibility_errors.py
@@ -0,0 +1,133 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Keep compatibility failure details out of MCP client messages."""
+
+from unittest.mock import AsyncMock, MagicMock, patch, PropertyMock
+
+import pytest
+from fastmcp import Client, Context
+from fastmcp.client.client import CallToolResult
+
+from superset.exceptions import QueryObjectValidationError
+from superset.mcp_service.app import mcp
+from superset.semantic_layers.dimension_resolution import
AmbiguousDimensionError
+from superset.utils import json
+
+
[email protected]("semantic_layers_enabled")
[email protected]
[email protected]("member", ["metrics", "dimensions"])
[email protected]("external", [False, True])
[email protected]("error_class", [RuntimeError,
QueryObjectValidationError])
+async def test_compatibility_errors_use_fixed_messages(
+ member: str,
+ external: bool,
+ error_class: type[Exception],
+ caplog: pytest.LogCaptureFixture,
+) -> None:
+ """Results and client notifications stay fixed while server diagnostics
survive."""
+ tool_name: str = f"get_compatible_{member}"
+ error: Exception = error_class("provider-private-detail")
+ view: MagicMock = MagicMock(metrics=[], columns=[])
+ getattr(view, tool_name).side_effect = error
+ user: MagicMock = MagicMock(id=1, username="admin")
+ message: str = f"Unable to retrieve compatible {member}."
+ notify: AsyncMock
+ client: Client
+ with (
+ patch("superset.mcp_service.auth.get_user_from_request",
return_value=user),
+ patch(
+ f"superset.mcp_service.semantic_layer.tool.{tool_name}"
+ ".user_can_view_data_model_metadata",
+ return_value=True,
+ ),
+ patch(
+ "superset.daos.dataset.DatasetDAO.find_by_id",
+ side_effect=error,
+ ),
+ patch(
+ "superset.daos.semantic_layer.SemanticViewDAO.find_by_id",
+ return_value=view,
+ ),
+ patch.object(Context, "error", new_callable=AsyncMock) as notify,
+ ):
+ async with Client(mcp) as client:
+ result: CallToolResult = await client.call_tool(
+ tool_name,
+ {"request": {"view_id" if external else "dataset_id": 5}},
+ )
+ data: dict[str, object] = json.loads(result.content[0].text)
+
+ assert data["success"] is False
+ assert data["error_type"] == "InternalError"
+ assert data["error"] == message
+ assert data["message"] == message
+ assert "provider-private-detail" not in str(result)
+ notify.assert_awaited_once_with(message)
+ assert f"{error_class.__name__}: provider-private-detail" in caplog.text
+
+
[email protected]("semantic_layers_enabled")
[email protected]
[email protected]("member", ["metrics", "dimensions"])
[email protected]("failure_site", ["columns", "compatibility"])
+async def test_compatibility_preserves_host_ambiguity(
+ member: str, failure_site: str
+) -> None:
+ """Authorized clients receive actionable host validation, not provider
errors."""
+ tool_name: str = f"get_compatible_{member}"
+ message: str = "Semantic dimension 'x' has ambiguous variants for grain
'day'."
+ error: AmbiguousDimensionError = AmbiguousDimensionError(message)
+ view: MagicMock = MagicMock(metrics=[], columns=[])
+ if failure_site == "compatibility":
+ getattr(view, tool_name).side_effect = error
+ user: MagicMock = MagicMock(id=1, username="admin")
+ notify: AsyncMock
+ client: Client
+ with (
+ patch("superset.mcp_service.auth.get_user_from_request",
return_value=user),
+ patch(
+ f"superset.mcp_service.semantic_layer.tool.{tool_name}"
+ ".user_can_view_data_model_metadata",
+ return_value=True,
+ ),
+ patch(
+ "superset.daos.semantic_layer.SemanticViewDAO.find_by_id",
+ return_value=view,
+ ),
+ patch.object(
+ type(view),
+ "columns",
+ new_callable=PropertyMock,
+ create=True,
+ return_value=[],
+ side_effect=error if failure_site == "columns" else None,
+ ),
+ patch.object(Context, "error", new_callable=AsyncMock) as notify,
+ ):
+ async with Client(mcp) as client:
+ result: CallToolResult = await client.call_tool(
+ tool_name, {"request": {"view_id": 5}}
+ )
+ data: dict[str, object] = json.loads(result.content[0].text)
+
+ view.raise_for_access.assert_called_once_with()
+ assert data["success"] is False
+ assert data["error_type"] == "ValidationError"
+ assert data["error"] == message
+ assert data["message"] == message
+ notify.assert_not_awaited()
diff --git
a/tests/unit_tests/mcp_service/semantic_layer/tool/test_list_metrics.py
b/tests/unit_tests/mcp_service/semantic_layer/tool/test_list_metrics.py
index 597b55783ca..9014ba454a2 100644
--- a/tests/unit_tests/mcp_service/semantic_layer/tool/test_list_metrics.py
+++ b/tests/unit_tests/mcp_service/semantic_layer/tool/test_list_metrics.py
@@ -24,17 +24,18 @@ import importlib
from collections.abc import Generator
from types import ModuleType
from typing import Any
-from unittest.mock import call, MagicMock, Mock, patch, PropertyMock
+from unittest.mock import AsyncMock, call, MagicMock, Mock, patch, PropertyMock
import pyarrow as pa
import pytest
-from fastmcp import Client, FastMCP
+from fastmcp import Client, Context, FastMCP
+from fastmcp.client.client import CallToolResult
from fastmcp.exceptions import ToolError
from pydantic import ValidationError
from superset_core.semantic_layers.types import Dimension, Grains
from superset.errors import ErrorLevel, SupersetError, SupersetErrorType
-from superset.exceptions import SupersetSecurityException
+from superset.exceptions import QueryObjectValidationError,
SupersetSecurityException
from superset.mcp_service.app import mcp
from superset.mcp_service.constants import DEFAULT_MAX_RESPONSE_BYTES
from superset.mcp_service.middleware import (
@@ -43,6 +44,7 @@ from superset.mcp_service.middleware import (
)
from superset.mcp_service.semantic_layer.schemas import ListMetricsRequest
from superset.mcp_service.utils.response_size_utils import
get_response_size_bytes
+from superset.semantic_layers.dimension_resolution import
AmbiguousDimensionError
from superset.semantic_layers.models import SemanticView
from superset.utils import json
@@ -776,3 +778,100 @@ async def
test_list_metrics_page_beyond_last_page_returns_empty(
assert data["total_count"] == 2
assert data["page"] == 9999
assert data["total_pages"] == 1
+
+
[email protected]
[email protected]("error_class", [RuntimeError,
QueryObjectValidationError])
+async def test_list_metrics_unexpected_error_is_sanitized(
+ mcp_server: FastMCP,
+ error_class: type[Exception],
+ caplog: pytest.LogCaptureFixture,
+) -> None:
+ """Outer failures retain diagnostics only in server logs, not MCP
messages."""
+ error: Exception = error_class("provider-private-detail")
+ message: str = "Unable to retrieve metrics."
+ notify: AsyncMock
+ client: Client
+ with (
+ patch.object(list_metrics_module.DatasetDAO, "find_by_id",
side_effect=error),
+ patch.object(Context, "error", new_callable=AsyncMock) as notify,
+ ):
+ async with Client(mcp_server) as client:
+ result: CallToolResult = await client.call_tool(
+ "list_metrics", {"request": {"dataset_id": 42}}
+ )
+ data: dict[str, object] = json.loads(result.content[0].text)
+
+ assert data["success"] is False
+ assert data["error_type"] == "InternalError"
+ assert data["error"] == message
+ assert data["message"] == message
+ assert "provider-private-detail" not in str(result)
+ notify.assert_awaited_once_with(message)
+ assert f"{error_class.__name__}: provider-private-detail" in caplog.text
+
+
[email protected]("semantic_layers_enabled")
[email protected]
[email protected](
+ "error_class", [RuntimeError, QueryObjectValidationError,
AmbiguousDimensionError]
+)
[email protected]("failure_site", ["columns", "compatibility"])
+async def test_list_metrics_partial_failure_keeps_only_safe_warning(
+ mcp_server: FastMCP,
+ error_class: type[Exception],
+ failure_site: str,
+ caplog: pytest.LogCaptureFixture,
+) -> None:
+ """One broken view does not hide healthy metrics or expose provider
details."""
+ safe: bool = error_class is AmbiguousDimensionError
+ detail: str = (
+ "Semantic dimension 'x' has ambiguous variants for grain 'day'."
+ if safe
+ else "provider-private-detail"
+ )
+ error: Exception = error_class(detail)
+ view: MagicMock = _make_view(5)
+ healthy_view: MagicMock = _make_view(6)
+ if failure_site == "compatibility":
+ view.get_compatible_dimensions.side_effect = error
+ warning: AsyncMock
+ notify: AsyncMock
+ daos: tuple[MagicMock, MagicMock, MagicMock]
+ client: Client
+ with (
+ _patched_dataset_search([]) as daos,
+ patch.object(
+ type(view),
+ "columns",
+ new_callable=PropertyMock,
+ create=True,
+ return_value=view.columns,
+ side_effect=error if failure_site == "columns" else None,
+ ),
+ patch.object(Context, "warning", new_callable=AsyncMock) as warning,
+ patch.object(Context, "error", new_callable=AsyncMock) as notify,
+ ):
+ daos[1].find_accessible.return_value = [view, healthy_view]
+ async with Client(mcp_server) as client:
+ result: CallToolResult = await client.call_tool(
+ "list_metrics",
+ {"request": {"include_compatible_dimensions": True,
"page_size": 4}},
+ )
+ data: dict[str, Any] = json.loads(result.content[0].text)
+
+ assert data["success"] is True
+ assert data["total_count"] == 2
+ assert {metric["view_id"] for metric in data["metrics"]} == {6}
+ assert {metric["name"] for metric in data["metrics"]} == {"bookings",
"revenue"}
+ notify.assert_not_awaited()
+ if safe:
+ warning.assert_awaited_once_with(
+ f"Could not load metrics for view id=5: {detail}"
+ )
+ else:
+ warning.assert_awaited_once_with(
+ "Unable to retrieve metrics for semantic view id=5."
+ )
+ assert "provider-private-detail" not in str(result)
+ assert f"{error_class.__name__}: provider-private-detail" in
caplog.text
diff --git a/tests/unit_tests/semantic_layers/api_test.py
b/tests/unit_tests/semantic_layers/api_test.py
index a2389757258..ee2a158af36 100644
--- a/tests/unit_tests/semantic_layers/api_test.py
+++ b/tests/unit_tests/semantic_layers/api_test.py
@@ -674,21 +674,22 @@ def test_runtime_schema_unknown_type(
@SEMANTIC_LAYERS_APP
def test_runtime_schema_exception(
- client: Any,
+ client: FlaskClient,
full_api_access: None,
mocker: MockerFixture,
+ caplog: pytest.LogCaptureFixture,
) -> None:
- """Test POST /<uuid>/schema/runtime returns 400 when schema raises."""
- test_uuid = str(uuid_lib.uuid4())
- mock_layer = MagicMock()
+ """Keep provider details in server logs, not the runtime-schema
response."""
+ test_uuid: str = str(uuid_lib.uuid4())
+ mock_layer: MagicMock = MagicMock()
mock_layer.type = "snowflake"
mock_layer.implementation.configuration = {"account": "test"}
- mock_dao = mocker.patch("superset.semantic_layers.api.SemanticLayerDAO")
+ mock_dao: MagicMock =
mocker.patch("superset.semantic_layers.api.SemanticLayerDAO")
mock_dao.find_by_uuid.return_value = mock_layer
- mock_cls = MagicMock()
- mock_cls.get_runtime_schema.side_effect = ValueError("Bad config")
+ mock_cls: MagicMock = MagicMock()
+ mock_cls.get_runtime_schema.side_effect =
ValueError("provider-private-detail")
mocker.patch.dict(
"superset.semantic_layers.api.registry",
@@ -696,12 +697,14 @@ def test_runtime_schema_exception(
clear=True,
)
- response = client.post(
+ response: TestResponse = client.post(
f"/api/v1/semantic_layer/{test_uuid}/schema/runtime",
)
assert response.status_code == 400
- assert "Bad config" in response.json["message"]
+ assert response.json["message"] == "Unable to fetch semantic layer runtime
schema."
+ assert "provider-private-detail" not in response.get_data(as_text=True)
+ assert "ValueError: provider-private-detail" in caplog.text
@pytest.mark.parametrize(
@@ -3051,26 +3054,31 @@ def test_get_semantic_view_structure_feature_flag_off(
@SEMANTIC_LAYERS_APP
[email protected]("method", ["get_dimensions", "get_metrics"])
def test_get_semantic_view_structure_implementation_error(
- client: Any,
+ client: FlaskClient,
full_api_access: None,
mocker: MockerFixture,
+ caplog: pytest.LogCaptureFixture,
+ method: str,
) -> None:
- """Test GET /<pk>/structure returns 422 when implementation raises."""
- mock_view = MagicMock()
- mock_view.implementation.get_dimensions.side_effect = RuntimeError(
- "Connection failed"
+ """Keep provider details in server logs, not the structure response."""
+ mock_view: MagicMock = MagicMock()
+ getattr(mock_view.implementation, method).side_effect = RuntimeError(
+ "provider-private-detail"
)
- mock_db_session = mocker.patch("superset.semantic_layers.api.db.session")
+ mock_db_session: MagicMock =
mocker.patch("superset.semantic_layers.api.db.session")
mock_db_session.query.return_value.filter_by.return_value.first.return_value = (
mock_view
)
- response = client.get("/api/v1/semantic_view/1/structure")
+ response: TestResponse = client.get("/api/v1/semantic_view/1/structure")
assert response.status_code == 422
- assert "Connection failed" in response.json["message"]
+ assert response.json["message"] == "Unable to fetch semantic view
structure."
+ assert "provider-private-detail" not in response.get_data(as_text=True)
+ assert "RuntimeError: provider-private-detail" in caplog.text
@SEMANTIC_LAYERS_APP