This is an automated email from the ASF dual-hosted git repository.
sadpandajoe 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 b82ff27f9cc fix(sqllab): render each query error once on async
fallback (#42318)
b82ff27f9cc is described below
commit b82ff27f9cc18f559ca9eed478ba1cdb0a93de7c
Author: Joe Li <[email protected]>
AuthorDate: Fri Jul 24 14:13:35 2026 -0700
fix(sqllab): render each query error once on async fallback (#42318)
Co-authored-by: Claude Opus 4.8 <[email protected]>
Co-authored-by: Evan Rusackas <[email protected]>
---
.../SqlLab/components/ResultSet/ResultSet.test.tsx | 29 ++++++++++++++++++++++
.../src/SqlLab/components/ResultSet/index.tsx | 21 +++++++++++++++-
2 files changed, 49 insertions(+), 1 deletion(-)
diff --git
a/superset-frontend/src/SqlLab/components/ResultSet/ResultSet.test.tsx
b/superset-frontend/src/SqlLab/components/ResultSet/ResultSet.test.tsx
index 99388fa7ca3..63a239ddcd5 100644
--- a/superset-frontend/src/SqlLab/components/ResultSet/ResultSet.test.tsx
+++ b/superset-frontend/src/SqlLab/components/ResultSet/ResultSet.test.tsx
@@ -355,6 +355,35 @@ describe('ResultSet', () => {
expect(errorMessages).toHaveLength(errors.length);
});
+ test('deduplicates errors present in both query.errors and
query.extra.errors', async () => {
+ // The async fallback path (QueryAutoRefresh) populates both
+ // `query.errors` (via queryFailed) and `query.extra.errors` (via
+ // refreshQueries) with the same errors. ResultSet should render each
+ // error only once, not twice.
+ const duplicatedErrorQuery = {
+ ...failedQueryWithErrors,
+ extra: { errors: failedQueryWithErrors.errors },
+ };
+ const duplicatedErrorState = {
+ ...initialState,
+ sqlLab: {
+ ...initialState.sqlLab,
+ queries: {
+ [duplicatedErrorQuery.id]: duplicatedErrorQuery,
+ },
+ },
+ };
+
+ await waitFor(() => {
+ setup(
+ { ...mockedProps, queryId: duplicatedErrorQuery.id },
+ mockStore(duplicatedErrorState),
+ );
+ });
+ const errorMessages = screen.getAllByTestId('error-message');
+ expect(errorMessages).toHaveLength(failedQueryWithErrors.errors.length);
+ });
+
test('should render a timeout error with a retrial button', async () => {
await waitFor(() => {
setup(
diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
index 7fc71d13534..a4c24a34c80 100644
--- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
+++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
@@ -618,7 +618,26 @@ const ResultSet = ({
}
if (query.state === QueryState.Failed) {
- const errors = [...(query.extra?.errors || []), ...(query.errors || [])];
+ // A failed query can carry the same error in both `query.errors` (set by
+ // the queryFailed action) and `query.extra.errors` (set by the async
+ // refresh). When falling back to async polling both get populated with
+ // identical errors, so deduplicate to avoid rendering each error twice.
+ const seenErrors = new Set<string>();
+ const errors = [
+ ...(query.extra?.errors || []),
+ ...(query.errors || []),
+ ].filter(error => {
+ // json-bigint parsing can leave native BigInt values in payloads,
+ // which JSON.stringify rejects; serialize them as strings.
+ const key = JSON.stringify(error, (_, value) =>
+ typeof value === 'bigint' ? value.toString() : value,
+ );
+ if (seenErrors.has(key)) {
+ return false;
+ }
+ seenErrors.add(key);
+ return true;
+ });
return (
<ResultlessStyles>