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

rusackas 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 bbf0fa5f42f fix(core): stop discarding API errors that quote an HTML 
tag (#42489)
bbf0fa5f42f is described below

commit bbf0fa5f42fbb3e3a96f3d82074b236f0700ef00
Author: Gaurav Dubey <[email protected]>
AuthorDate: Thu Oct 1 05:33:04 2026 +0530

    fix(core): stop discarding API errors that quote an HTML tag (#42489)
    
    Co-authored-by: Evan Rusackas <[email protected]>
    Co-authored-by: Evan Rusackas <[email protected]>
    Co-authored-by: Claude Sonnet 5 <[email protected]>
---
 .../src/query/getClientErrorObject.ts              | 29 +++++++++-
 .../test/query/getClientErrorObject.test.ts        | 66 ++++++++++++++++++++++
 2 files changed, 94 insertions(+), 1 deletion(-)

diff --git 
a/superset-frontend/packages/superset-ui-core/src/query/getClientErrorObject.ts 
b/superset-frontend/packages/superset-ui-core/src/query/getClientErrorObject.ts
index eb96ab28d05..454fd4999c4 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/query/getClientErrorObject.ts
+++ 
b/superset-frontend/packages/superset-ui-core/src/query/getClientErrorObject.ts
@@ -89,7 +89,34 @@ const ERROR_CODE_LOOKUP = {
 };
 
 export function checkForHtml(str: string): boolean {
-  return !isJsonString(str) && isProbablyHTML(str);
+  // An HTML error page served by a proxy or gateway begins with markup, and
+  // has no message worth showing. A server-authored error that merely quotes
+  // a tag — a database syntax error echoing back the offending `<a>`, say —
+  // begins with prose, and is the whole point of the response. Only the
+  // former may be collapsed into a generic status message.
+  //
+  // A leading `<` alone isn't enough: a message that opens with a quoted,
+  // unclosed tag (`<a> is not valid syntax`) also starts with `<`, and
+  // isProbablyHTML()'s DOMParser auto-closes stray tags on parse, so it
+  // can't tell that case apart either. Requiring the leading tag to actually
+  // close somewhere in the string does: a real fragment like
+  // `<div>500: Internal Server Error</div>` closes what it opens, a quoted
+  // tag doesn't. A `<!doctype html>` preamble is exempt from this check
+  // since isProbablyHTML() already gates that case on its own.
+  if (isJsonString(str)) {
+    return false;
+  }
+  const trimmed = str.trimStart();
+  if (!trimmed.startsWith('<')) {
+    return false;
+  }
+  if (!/^<!doctype html>/i.test(trimmed)) {
+    const leadingTag = trimmed.match(/^<([a-z][a-z0-9]*)\b/i)?.[1];
+    if (!leadingTag || !new RegExp(`</${leadingTag}\\s*>`, 'i').test(str)) {
+      return false;
+    }
+  }
+  return isProbablyHTML(str);
 }
 
 export function parseStringResponse(str: string): string {
diff --git 
a/superset-frontend/packages/superset-ui-core/test/query/getClientErrorObject.test.ts
 
b/superset-frontend/packages/superset-ui-core/test/query/getClientErrorObject.test.ts
index f763f9c6ff5..375b97f71ef 100644
--- 
a/superset-frontend/packages/superset-ui-core/test/query/getClientErrorObject.test.ts
+++ 
b/superset-frontend/packages/superset-ui-core/test/query/getClientErrorObject.test.ts
@@ -311,6 +311,72 @@ test('parseErrorJson with HTML message and status code', 
() => {
   });
 });
 
+test('parseErrorJson keeps a server message that only quotes an HTML tag', () 
=> {
+  // A database driver echoes the offending fragment back in its syntax error.
+  // The message is prose, not an HTML error page, so it must survive intact
+  // instead of collapsing into the status-code message.
+  const message =
+    'Error: HTTPDriver received ClickHouse error code 62. DB::Exception: ' +
+    "Syntax error: failed at position 37 ('<') (line 1, col 37): <a> AS " +
+    '`My column_b77020` FROM (select number from numbers(10)) AS ' +
+    '`virtual_table` LIMIT 1000 FORMAT Native.';
+
+  expect(parseErrorJson({ status: 400, message })).toEqual({
+    status: 400,
+    message,
+    error: message,
+  });
+
+  // An actual error page still collapses, even when the server pads it with
+  // leading whitespace.
+  const page = '\n  <!doctype html><title>502 Bad Gateway</title>';
+  expect(parseErrorJson({ status: 502, message: page })).toEqual({
+    status: 502,
+    message: page,
+    error: 'Bad gateway',
+  });
+});
+
+test('parseErrorJson keeps a server message that opens with an unclosed HTML 
tag', () => {
+  // The tag itself is never closed anywhere in the string, unlike a real
+  // fragment such as `<div>...</div>`, so this isn't an HTML error page —
+  // just a message that happens to start by quoting the offending markup.
+  const message = '<a> is not valid syntax';
+
+  expect(parseErrorJson({ status: 400, message })).toEqual({
+    status: 400,
+    message,
+    error: message,
+  });
+});
+
+test('parseErrorJson keeps a message that starts with < but is not a tag', () 
=> {
+  // No leading tag name to match at all, so this never reaches the
+  // closing-tag check; it must still be treated as plain prose.
+  const message = '<3 is not an HTML tag';
+
+  expect(parseErrorJson({ status: 400, message })).toEqual({
+    status: 400,
+    message,
+    error: message,
+  });
+});
+
+test('parseErrorJson keeps a message that itself parses as JSON', () => {
+  // isJsonString() short-circuits checkForHtml before the tag checks ever
+  // run: a message that happens to parse as JSON on its own is never
+  // treated as an HTML error page, whatever text it contains — otherwise
+  // isProbablyHTML()'s full-string scan would flag the embedded tag below
+  // and incorrectly collapse this into a generic status message.
+  const message = '{"detail": "<div>not actually markup</div>"}';
+
+  expect(parseErrorJson({ status: 400, message })).toEqual({
+    status: 400,
+    message,
+    error: message,
+  });
+});
+
 test('parseErrorJson with stacktrace', () => {
   expect(
     parseErrorJson({ error: 'error message', stack: 'stacktrace' }),

Reply via email to