Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/common/errors.ts
5236 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
// eslint-disable-next-line local/code-no-any-casts
143
const stack: string = (<any>error).stacktrace || (<any>error).stack;
144
return {
145
$isError: true,
146
name,
147
message,
148
stack,
149
noTelemetry: ErrorNoTelemetry.isErrorNoTelemetry(error),
150
cause: cause ? transformErrorForSerialization(cause) : undefined,
151
code: (<ErrorWithCode>error).code
152
};
153
}
154
155
// return as is
156
return error;
157
}
158
159
export function transformErrorFromSerialization(data: SerializedError): Error {
160
let error: Error;
161
if (data.noTelemetry) {
162
error = new ErrorNoTelemetry();
163
} else {
164
error = new Error();
165
error.name = data.name;
166
}
167
error.message = data.message;
168
error.stack = data.stack;
169
if (data.code) {
170
(<ErrorWithCode>error).code = data.code;
171
}
172
if (data.cause) {
173
error.cause = transformErrorFromSerialization(data.cause);
174
}
175
return error;
176
}
177
178
// see https://github.com/v8/v8/wiki/Stack%20Trace%20API#basic-stack-traces
179
export interface V8CallSite {
180
getThis(): unknown;
181
getTypeName(): string | null;
182
getFunction(): Function | undefined;
183
getFunctionName(): string | null;
184
getMethodName(): string | null;
185
getFileName(): string | null;
186
getLineNumber(): number | null;
187
getColumnNumber(): number | null;
188
getEvalOrigin(): string | undefined;
189
isToplevel(): boolean;
190
isEval(): boolean;
191
isNative(): boolean;
192
isConstructor(): boolean;
193
toString(): string;
194
}
195
196
export const canceledName = 'Canceled';
197
198
/**
199
* Checks if the given error is a promise in canceled state
200
*/
201
export function isCancellationError(error: any): boolean {
202
if (error instanceof CancellationError) {
203
return true;
204
}
205
return error instanceof Error && error.name === canceledName && error.message === canceledName;
206
}
207
208
// !!!IMPORTANT!!!
209
// Do NOT change this class because it is also used as an API-type.
210
export class CancellationError extends Error {
211
constructor() {
212
super(canceledName);
213
this.name = this.message;
214
}
215
}
216
217
export class PendingMigrationError extends Error {
218
219
private static readonly _name = 'PendingMigrationError';
220
221
static is(error: unknown): error is PendingMigrationError {
222
return error instanceof PendingMigrationError || (error instanceof Error && error.name === PendingMigrationError._name);
223
}
224
225
constructor(message: string) {
226
super(message);
227
this.name = PendingMigrationError._name;
228
}
229
}
230
231
/**
232
* @deprecated use {@link CancellationError `new CancellationError()`} instead
233
*/
234
export function canceled(): Error {
235
const error = new Error(canceledName);
236
error.name = error.message;
237
return error;
238
}
239
240
export function illegalArgument(name?: string): Error {
241
if (name) {
242
return new Error(`Illegal argument: ${name}`);
243
} else {
244
return new Error('Illegal argument');
245
}
246
}
247
248
export function illegalState(name?: string): Error {
249
if (name) {
250
return new Error(`Illegal state: ${name}`);
251
} else {
252
return new Error('Illegal state');
253
}
254
}
255
256
export class ReadonlyError extends TypeError {
257
constructor(name?: string) {
258
super(name ? `${name} is read-only and cannot be changed` : 'Cannot change read-only property');
259
}
260
}
261
262
export function getErrorMessage(err: any): string {
263
if (!err) {
264
return 'Error';
265
}
266
267
if (err.message) {
268
return err.message;
269
}
270
271
if (err.stack) {
272
return err.stack.split('\n')[0];
273
}
274
275
return String(err);
276
}
277
278
export class NotImplementedError extends Error {
279
constructor(message?: string) {
280
super('NotImplemented');
281
if (message) {
282
this.message = message;
283
}
284
}
285
}
286
287
export class NotSupportedError extends Error {
288
constructor(message?: string) {
289
super('NotSupported');
290
if (message) {
291
this.message = message;
292
}
293
}
294
}
295
296
export class ExpectedError extends Error {
297
readonly isExpected = true;
298
}
299
300
/**
301
* Error that when thrown won't be logged in telemetry as an unhandled error.
302
*/
303
export class ErrorNoTelemetry extends Error {
304
override readonly name: string;
305
306
constructor(msg?: string) {
307
super(msg);
308
this.name = 'CodeExpectedError';
309
}
310
311
public static fromError(err: Error): ErrorNoTelemetry {
312
if (err instanceof ErrorNoTelemetry) {
313
return err;
314
}
315
316
const result = new ErrorNoTelemetry();
317
result.message = err.message;
318
result.stack = err.stack;
319
return result;
320
}
321
322
public static isErrorNoTelemetry(err: Error): err is ErrorNoTelemetry {
323
return err.name === 'CodeExpectedError';
324
}
325
}
326
327
/**
328
* This error indicates a bug.
329
* Do not throw this for invalid user input.
330
* Only catch this error to recover gracefully from bugs.
331
*/
332
export class BugIndicatingError extends Error {
333
constructor(message?: string) {
334
super(message || 'An unexpected bug occurred.');
335
Object.setPrototypeOf(this, BugIndicatingError.prototype);
336
337
// Because we know for sure only buggy code throws this,
338
// we definitely want to break here and fix the bug.
339
// debugger;
340
}
341
}
342
343