/** * Make a string out of an error (or other equivalents), * including any additional data such as stack trace if available. * Safe to use on unknown inputs. */ export function stringifyError(error: unknown, noStack = false): string { const o = stringifyErrorInner(error); if (noStack || !o.stack) { return o.message; } return `${o.message}, ${o.stack}`; } export function stringifyErrorInner(error: unknown): { message: string; stack: string | undefined; } { let message: string | undefined; let stack: string | undefined; if (typeof error === "string") { message = error; } else if (error === null) { message = "null"; } else if (error === undefined) { message = "undefined"; } else if (error && typeof error === "object") { if ( typeof (error as any).error === "object" && (error as any).error.message ) { message = (error as any).error.message; stack = (error as any).error.stack; } else if ((error as any).reason) { if ((error as any).reason.message) { message = (error as any).reason.message; stack = (error as any).reason.stack || (error as any).reason.reason; } else { // Is a Meteor.Error message = (error as any).reason; stack = (error as Error).stack; } } else if ((error as Error).message) { // Is an Error message = (error as Error).message; stack = (error as Error).stack; } else if ((error as any).details) { message = (error as any).details; } else { try { // Try to stringify the object: message = JSON.stringify(error); } catch (e: unknown) { // eslint-disable-next-line @typescript-eslint/no-base-to-string,@typescript-eslint/restrict-template-expressions message = `${error} (stringifyError: ${e})`; } } } else { // eslint-disable-next-line @typescript-eslint/no-base-to-string,@typescript-eslint/restrict-template-expressions message = `${error}`; } message = `${message}`; return { message, stack, }; }