Repository navigation
Expand file tree
/
Copy pathparseError.ts
More file actions
134 lines (114 loc) · 3.58 KB
/
Copy pathparseError.ts
File metadata and controls
134 lines (114 loc) · 3.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/**
* Utilities for parsing and classifying error messages,
* particularly ClickHouse DB errors that can be verbose HTML
* or include DB::Exception stack traces.
*
* Mirrors the error handling patterns from the web frontend:
* @source packages/app/src/DBSearchPage.tsx (queryError rendering)
* @source packages/app/src/components/DBTableChart.tsx (ClickHouseQueryError handling)
*/
import { ClickHouseQueryError } from '@hyperdx/common-utils/dist/clickhouse';
export type ErrorSeverity = 'error' | 'warning';
export interface ParsedError {
severity: ErrorSeverity;
/** Human-readable summary of the error */
message: string;
/** The original SQL query that caused the error (from ClickHouseQueryError) */
query?: string;
}
const MAX_MESSAGE_LENGTH = 500;
/**
* Extract a readable message from a ClickHouse DB::Exception string.
*
* ClickHouse errors typically look like:
* "Code: 62. DB::Exception: Syntax error: failed at position 5 ... (SYNTAX_ERROR) (version 24.8.1.1)"
*
* We extract everything up to the first "(version" or the error code suffix.
*/
function parseClickHouseException(raw: string): string {
// Strip the leading "Code: NNN. " prefix for readability
let msg = raw.replace(/^Code:\s*\d+\.\s*/, '');
// Strip "DB::Exception: " prefix
msg = msg.replace(/^DB::Exception:\s*/, '');
// Strip trailing version info — e.g. "(version 24.8.1.1)"
msg = msg.replace(/\s*\(version\s+[^)]+\)\s*$/, '');
// Strip stack trace lines (lines starting with numbers like "0. ...")
msg = msg
.split('\n')
.filter(line => !/^\d+\.\s+0x/.test(line.trim()))
.join('\n')
.trim();
return msg;
}
/**
* Strip HTML tags from a string — ClickHouse proxy errors sometimes
* return full HTML error pages.
*/
function stripHtml(raw: string): string {
// Extract text from <title> if present (often has the best summary)
const titleMatch = raw.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
if (titleMatch) {
return titleMatch[1].trim();
}
// Fall back to stripping all tags
return raw
.replace(/<[^>]+>/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}
/**
* Clean up a raw error message string (handle HTML, DB::Exception, truncation).
*/
function cleanMessage(raw: string): string {
let message = raw.trim();
// Handle HTML error responses
if (message.startsWith('<!') || message.startsWith('<html')) {
message = stripHtml(message);
}
// Handle ClickHouse DB::Exception
if (message.includes('DB::Exception')) {
message = parseClickHouseException(message);
}
// Truncate if still too long
if (message.length > MAX_MESSAGE_LENGTH) {
message = message.slice(0, MAX_MESSAGE_LENGTH) + '…';
}
return message;
}
/**
* Parse an error into a structured ParsedError.
*
* Accepts:
* - `ClickHouseQueryError` — extracts both `.message` and `.query`
* (mirrors DBSearchPage.tsx and DBTableChart.tsx patterns)
* - `Error` — uses `.message`
* - `string` — raw message with heuristic parsing
*/
export function parseError(
err: string | Error | ClickHouseQueryError,
severity: ErrorSeverity = 'error',
): ParsedError {
if (!err) {
return { severity, message: 'An unknown error occurred.' };
}
// ClickHouseQueryError — structured error with query context
if (err instanceof ClickHouseQueryError) {
return {
severity,
message: cleanMessage(err.message),
query: err.query,
};
}
// Generic Error object
if (err instanceof Error) {
return {
severity,
message: cleanMessage(err.message),
};
}
// Raw string
return {
severity,
message: cleanMessage(err),
};
}