Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/common/errors.ts
3291 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
export interface ErrorListenerCallback {
7
(error: any): void;
8
}
9
10
export interface ErrorListenerUnbind {
11
(): void;
12
}
13
14
// Avoid circular dependency on EventEmitter by implementing a subset of the interface.
15
export class ErrorHandler {
16
private unexpectedErrorHandler: (e: any) => void;
17
private listeners: ErrorListenerCallback[];
18
19
constructor() {
20
21
this.listeners = [];
22
23
this.unexpectedErrorHandler = function (e: any) {
24
setTimeout(() => {
25
if (e.stack) {
26
if (ErrorNoTelemetry.isErrorNoTelemetry(e)) {
27
throw new ErrorNoTelemetry(e.message + '\n\n' + e.stack);
28
}
29
30
throw new Error(e.message + '\n\n' + e.stack);
31
}
32
33
throw e;
34
}, 0);
35
};
36
}
37
38
addListener(listener: ErrorListenerCallback): ErrorListenerUnbind {
39
this.listeners.push(listener);
40
41
return () => {
42
this._removeListener(listener);
43
};
44
}
45
46
private emit(e: any): void {
47
this.listeners.forEach((listener) => {
48
listener(e);
49
});
50
}
51
52
private _removeListener(listener: ErrorListenerCallback): void {
53
this.listeners.splice(this.listeners.indexOf(listener), 1);
54
}
55
56
setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void {
57
this.unexpectedErrorHandler = newUnexpectedErrorHandler;
58
}
59
60
getUnexpectedErrorHandler(): (e: any) => void {
61
return this.unexpectedErrorHandler;
62
}
63
64
onUnexpectedError(e: any): void {
65
this.unexpectedErrorHandler(e);
66
this.emit(e);
67
}
68
69
// For external errors, we don't want the listeners to be called
70
onUnexpectedExternalError(e: any): void {
71
this.unexpectedErrorHandler(e);
72
}
73
}
74
75
export const errorHandler = new ErrorHandler();
76
77
/** @skipMangle */
78
export function setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void {
79
errorHandler.setUnexpectedErrorHandler(newUnexpectedErrorHandler);
80
}
81
82
/**
83
* Returns if the error is a SIGPIPE error. SIGPIPE errors should generally be
84
* logged at most once, to avoid a loop.
85
*
86
* @see https://github.com/microsoft/vscode-remote-release/issues/6481
87
*/
88
export function isSigPipeError(e: unknown): e is Error {
89
if (!e || typeof e !== 'object') {
90
return false;
91
}
92
93
const cast = e as Record<string, string | undefined>;
94
return cast.code === 'EPIPE' && cast.syscall?.toUpperCase() === 'WRITE';
95
}
96
97
/**
98
* This function should only be called with errors that indicate a bug in the product.
99
* E.g. buggy extensions/invalid user-input/network issues should not be able to trigger this code path.
100
* If they are, this indicates there is also a bug in the product.
101
*/
102
export function onBugIndicatingError(e: any): undefined {
103
errorHandler.onUnexpectedError(e);
104
return undefined;
105
}
106
107
export function onUnexpectedError(e: any): undefined {
108
// ignore errors from cancelled promises
109
if (!isCancellationError(e)) {
110
errorHandler.onUnexpectedError(e);
111
}
112
return undefined;
113
}
114
115
export function onUnexpectedExternalError(e: any): undefined {
116
// ignore errors from cancelled promises
117
if (!isCancellationError(e)) {
118
errorHandler.onUnexpectedExternalError(e);
119
}
120
return undefined;
121
}
122
123
export interface SerializedError {
124
readonly $isError: true;
125
readonly name: string;
126
readonly message: string;
127
readonly stack: string;
128
readonly noTelemetry: boolean;
129
readonly code?: string;
130
readonly cause?: SerializedError;
131
}
132
133
type ErrorWithCode = Error & {
134
code: string | undefined;
135
};
136
137
export function transformErrorForSerialization(error: Error): SerializedError;
138
export function transformErrorForSerialization(error: any): any;
139
export function transformErrorForSerialization(error: any): any {
140
if (error instanceof Error) {
141
const { name, message, cause } = error;
142
const stack: string = (<any>error).stacktrace || (<any>error).stack;
143
return {
144
$isError: true,
145
name,
146
message,
147
stack,
148
noTelemetry: ErrorNoTelemetry.isErrorNoTelemetry(error),
149
cause: cause ? transformErrorForSerialization(cause) : undefined,
150
code: (<ErrorWithCode>error).code
151
};
152
}
153
154
// return as is
155
return error;
156
}
157
158
export function transformErrorFromSerialization(data: SerializedError): Error {
159
let error: Error;
160
if (data.noTelemetry) {
161
error = new ErrorNoTelemetry();
162
} else {
163
error = new Error();
164
error.name = data.name;
165
}
166
error.message = data.message;
167
error.stack = data.stack;
168
if (data.code) {
169
(<ErrorWithCode>error).code = data.code;
170
}
171
if (data.cause) {
172
error.cause = transformErrorFromSerialization(data.cause);
173
}
174
return error;
175
}
176
177
// see https://github.com/v8/v8/wiki/Stack%20Trace%20API#basic-stack-traces
178
export interface V8CallSite {
179
getThis(): unknown;
180
getTypeName(): string | null;
181
getFunction(): Function | undefined;
182
getFunctionName(): string | null;
183
getMethodName(): string | null;
184
getFileName(): string | null;
185
getLineNumber(): number | null;
186
getColumnNumber(): number | null;
187
getEvalOrigin(): string | undefined;
188
isToplevel(): boolean;
189
isEval(): boolean;
190
isNative(): boolean;
191
isConstructor(): boolean;
192
toString(): string;
193
}
194
195
export const canceledName = 'Canceled';
196
197
/**
198
* Checks if the given error is a promise in canceled state
199
*/
200
export function isCancellationError(error: any): boolean {
201
if (error instanceof CancellationError) {
202
return true;
203
}
204
return error instanceof Error && error.name === canceledName && error.message === canceledName;
205
}
206
207
// !!!IMPORTANT!!!
208
// Do NOT change this class because it is also used as an API-type.
209
export class CancellationError extends Error {
210
constructor() {
211
super(canceledName);
212
this.name = this.message;
213
}
214
}
215
216
export class PendingMigrationError extends Error {
217
218
private static readonly _name = 'PendingMigrationError';
219
220
static is(error: unknown): error is PendingMigrationError {
221
return error instanceof PendingMigrationError || (error instanceof Error && error.name === PendingMigrationError._name);
222
}
223
224
constructor(message: string) {
225
super(message);
226
this.name = PendingMigrationError._name;
227
}
228
}
229
230
/**
231
* @deprecated use {@link CancellationError `new CancellationError()`} instead
232
*/
233
export function canceled(): Error {
234
const error = new Error(canceledName);
235
error.name = error.message;
236
return error;
237
}
238
239
export function illegalArgument(name?: string): Error {
240
if (name) {
241
return new Error(`Illegal argument: ${name}`);
242
} else {
243
return new Error('Illegal argument');
244
}
245
}
246
247
export function illegalState(name?: string): Error {
248
if (name) {
249
return new Error(`Illegal state: ${name}`);
250
} else {
251
return new Error('Illegal state');
252
}
253
}
254
255
export class ReadonlyError extends TypeError {
256
constructor(name?: string) {
257
super(name ? `${name} is read-only and cannot be changed` : 'Cannot change read-only property');
258
}
259
}
260
261
export function getErrorMessage(err: any): string {
262
if (!err) {
263
return 'Error';
264
}
265
266
if (err.message) {
267
return err.message;
268
}
269
270
if (err.stack) {
271
return err.stack.split('\n')[0];
272
}
273
274
return String(err);
275
}
276
277
export class NotImplementedError extends Error {
278
constructor(message?: string) {
279
super('NotImplemented');
280
if (message) {
281
this.message = message;
282
}
283
}
284
}
285
286
export class NotSupportedError extends Error {
287
constructor(message?: string) {
288
super('NotSupported');
289
if (message) {
290
this.message = message;
291
}
292
}
293
}
294
295
export class ExpectedError extends Error {
296
readonly isExpected = true;
297
}
298
299
/**
300
* Error that when thrown won't be logged in telemetry as an unhandled error.
301
*/
302
export class ErrorNoTelemetry extends Error {
303
override readonly name: string;
304
305
constructor(msg?: string) {
306
super(msg);
307
this.name = 'CodeExpectedError';
308
}
309
310
public static fromError(err: Error): ErrorNoTelemetry {
311
if (err instanceof ErrorNoTelemetry) {
312
return err;
313
}
314
315
const result = new ErrorNoTelemetry();
316
result.message = err.message;
317
result.stack = err.stack;
318
return result;
319
}
320
321
public static isErrorNoTelemetry(err: Error): err is ErrorNoTelemetry {
322
return err.name === 'CodeExpectedError';
323
}
324
}
325
326
/**
327
* This error indicates a bug.
328
* Do not throw this for invalid user input.
329
* Only catch this error to recover gracefully from bugs.
330
*/
331
export class BugIndicatingError extends Error {
332
constructor(message?: string) {
333
super(message || 'An unexpected bug occurred.');
334
Object.setPrototypeOf(this, BugIndicatingError.prototype);
335
336
// Because we know for sure only buggy code throws this,
337
// we definitely want to break here and fix the bug.
338
// debugger;
339
}
340
}
341
342