super-productivity/electron/error-handler-with-frontend-inform.ts
Johannes Millan e571d6e3bc fix(error-handling): prevent [object Object] from appearing in error messages
Improve error text extraction utilities to never return "[object Object]"
when displaying error messages to users. The fix adds more robust fallback
mechanisms including:

- Check for message, name, statusText properties before calling toString()
- Detect "[object Object]" result and fallback to JSON.stringify()
- Provide meaningful fallback messages when all extraction methods fail

Fixes #5790
2026-01-02 15:40:14 +01:00

103 lines
2.5 KiB
TypeScript

import { getIsAppReady, getWin } from './main-window';
import { IPC } from './shared-with-frontend/ipc-events.const';
import { error, log } from 'electron-log/main';
const WAIT_FOR_WIN_TIMEOUT_DURATION = 4000;
export const errorHandlerWithFrontendInform = (
e: Error | unknown | string = 'UNDEFINED ERROR',
additionalLogInfo?: unknown,
): void => {
const errObj = new Error(e as string);
if (_isReadyForFrontEndError()) {
_handleError(e, additionalLogInfo, errObj);
} else {
// try again a little later, when window might be ready
setTimeout(() => {
_handleError(e, additionalLogInfo, errObj);
}, WAIT_FOR_WIN_TIMEOUT_DURATION);
}
};
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
function _isReadyForFrontEndError(): boolean {
const mainWin = getWin();
const isAppReady = getIsAppReady();
return mainWin && mainWin.webContents && isAppReady;
}
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
function _handleError(
e: Error | unknown | string,
additionalLogInfo: unknown,
errObj: Error,
): void {
const mainWin = getWin();
const stack = errObj.stack;
console.error('ERR', e);
log(stack);
error(e, stack);
if (additionalLogInfo) {
log('Additional Error info: ', additionalLogInfo);
}
if (_isReadyForFrontEndError()) {
mainWin.webContents.send(IPC.ERROR, {
error: e,
errorStr: _getErrorStr(e),
stack,
});
} else {
error('Electron Error: Frontend not loaded. Could not send error to renderer.');
throw errObj;
}
}
const OBJECT_OBJECT_STR = '[object Object]';
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
function _getErrorStr(e: unknown): string {
if (typeof e === 'string') {
return e;
}
if (e == null) {
return 'Unknown error';
}
// Check for message property first (standard Error and custom errors)
if (typeof (e as any).message === 'string' && (e as any).message) {
return (e as any).message;
}
if (e instanceof Error) {
return e.toString();
}
// Check for name property
if (typeof (e as any).name === 'string' && (e as any).name) {
return (e as any).name;
}
if (typeof e === 'object') {
try {
const jsonStr = JSON.stringify(e);
if (jsonStr && jsonStr !== '{}') {
return jsonStr;
}
} catch {
// Circular reference - fall through
}
// Try toString but check for [object Object]
const str = String(e);
if (str && str !== OBJECT_OBJECT_STR) {
return str;
}
}
return 'Unknown error (unable to extract message)';
}