diff --git a/app/global_errors.ts b/app/global_errors.ts index bf103fe8c..66d79d3be 100644 --- a/app/global_errors.ts +++ b/app/global_errors.ts @@ -13,27 +13,28 @@ let quitText = 'Quit'; let copyErrorAndQuitText = 'Copy error and quit'; function handleError(prefix: string, error: Error): void { + const formattedError = Errors.toLogFormat(error); if (console._error) { - console._error(`${prefix}:`, Errors.toLogFormat(error)); + console._error(`${prefix}:`, formattedError); } - console.error(`${prefix}:`, Errors.toLogFormat(error)); + console.error(`${prefix}:`, formattedError); if (app.isReady()) { // title field is not shown on macOS, so we don't use it const buttonIndex = dialog.showMessageBoxSync({ buttons: [quitText, copyErrorAndQuitText], defaultId: 0, - detail: redactAll(error.stack || ''), + detail: redactAll(formattedError), message: prefix, noLink: true, type: 'error', }); if (buttonIndex === 1) { - clipboard.writeText(`${prefix}\n\n${redactAll(error.stack || '')}`); + clipboard.writeText(`${prefix}\n\n${redactAll(formattedError)}`); } } else { - dialog.showErrorBox(prefix, error.stack || ''); + dialog.showErrorBox(prefix, formattedError); } app.exit(1); diff --git a/app/main.ts b/app/main.ts index 3f7a2e360..9dc87748f 100644 --- a/app/main.ts +++ b/app/main.ts @@ -46,6 +46,7 @@ import { strictAssert } from '../ts/util/assert'; import { consoleLogger } from '../ts/util/consoleLogger'; import type { ThemeSettingType } from '../ts/types/StorageUIKeys'; import { ThemeType } from '../ts/types/Util'; +import * as Errors from '../ts/types/errors'; import './startup_config'; @@ -471,7 +472,7 @@ async function handleUrl(event: Electron.Event, rawTarget: string) { try { await shell.openExternal(target); } catch (error) { - getLogger().error(`Failed to open url: ${error.stack}`); + getLogger().error(`Failed to open url: ${Errors.toLogFormat(error)}`); } } } @@ -938,7 +939,7 @@ ipc.handle('database-ready', async () => { if (error) { getLogger().error( 'database-ready requested, but got sql error', - error && error.stack + Errors.toLogFormat(error) ); return; } @@ -1029,7 +1030,7 @@ async function readyForUpdates() { } catch (error) { getLogger().error( 'Error starting update checks:', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } @@ -1039,10 +1040,7 @@ async function forceUpdate() { getLogger().info('starting force update'); await updater.force(); } catch (error) { - getLogger().error( - 'Error during force update:', - error && error.stack ? error.stack : error - ); + getLogger().error('Error during force update:', Errors.toLogFormat(error)); } } @@ -1477,7 +1475,7 @@ const runSQLCorruptionHandler = async () => { `Restarting the application immediately. Error: ${error.message}` ); - await onDatabaseError(error.stack || error.message); + await onDatabaseError(Errors.toLogFormat(error)); }; async function initializeSQL( @@ -1796,7 +1794,7 @@ app.on('ready', async () => { } catch (err) { logger.error( 'main/ready: Error deleting temp dir:', - err && err.stack ? err.stack : err + Errors.toLogFormat(err) ); } @@ -1823,7 +1821,7 @@ app.on('ready', async () => { if (sqlError) { getLogger().error('sql.initialize was unsuccessful; returning early'); - await onDatabaseError(sqlError.stack || sqlError.message); + await onDatabaseError(Errors.toLogFormat(sqlError)); return; } @@ -1840,7 +1838,7 @@ app.on('ready', async () => { } catch (err) { getLogger().error( '(ready event handler) error deleting IndexedDB:', - err && err.stack ? err.stack : err + Errors.toLogFormat(err) ); } @@ -1949,10 +1947,7 @@ async function requestShutdown() { try { await request; } catch (error) { - getLogger().error( - 'requestShutdown error:', - error && error.stack ? error.stack : error - ); + getLogger().error('requestShutdown error:', Errors.toLogFormat(error)); } } @@ -2157,7 +2152,7 @@ ipc.handle( } catch (error) { getLogger().error( 'show-permissions-popup error:', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } @@ -2200,7 +2195,10 @@ ipc.on('get-built-in-images', async () => { if (mainWindow && mainWindow.webContents) { mainWindow.webContents.send('get-success-built-in-images', error.message); } else { - getLogger().error('Error handling get-built-in-images:', error.stack); + getLogger().error( + 'Error handling get-built-in-images:', + Errors.toLogFormat(error) + ); } } }); diff --git a/app/user_config.ts b/app/user_config.ts index 2e532a5be..1e04192ce 100644 --- a/app/user_config.ts +++ b/app/user_config.ts @@ -7,6 +7,7 @@ import { app } from 'electron'; import { start } from './base_config'; import config from './config'; +import * as Errors from '../ts/types/errors'; let userData: string | undefined; // Use separate data directory for benchmarks & development @@ -23,7 +24,7 @@ if (userData !== undefined) { try { mkdirSync(userData, { recursive: true }); } catch (error) { - console.error('Failed to create userData', error?.stack || String(error)); + console.error('Failed to create userData', Errors.toLogFormat(error)); } app.setPath('userData', userData); diff --git a/sticker-creator/components/StickerGrid.tsx b/sticker-creator/components/StickerGrid.tsx index 78693b818..84f3c6421 100644 --- a/sticker-creator/components/StickerGrid.tsx +++ b/sticker-creator/components/StickerGrid.tsx @@ -13,7 +13,9 @@ import type { Props as DropZoneProps } from '../elements/DropZone'; import { DropZone } from '../elements/DropZone'; import { processStickerImage } from '../util/preload'; import { useI18n } from '../util/i18n'; +import { ProcessStickerImageError } from '../errors'; import { MINUTE } from '../../ts/util/durations'; +import * as Errors from '../../ts/types/errors'; const queue = new PQueue({ concurrency: 3, timeout: MINUTE * 30 }); @@ -64,13 +66,16 @@ const InnerGrid = SortableContainer( } catch (e) { window.SignalContext.log.error( 'Error processing image:', - e?.stack ? e.stack : String(e) + Errors.toLogFormat(e) ); actions.removeSticker(path); + + const key = + e instanceof ProcessStickerImageError + ? e.errorMessageI18nKey + : 'StickerCreator--Toasts--errorProcessing'; actions.addToast({ - key: - (e || {}).errorMessageI18nKey || - 'StickerCreator--Toasts--errorProcessing', + key, }); } }); diff --git a/sticker-creator/errors.ts b/sticker-creator/errors.ts new file mode 100644 index 000000000..5044ca268 --- /dev/null +++ b/sticker-creator/errors.ts @@ -0,0 +1,8 @@ +// Copyright 2022 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +export class ProcessStickerImageError extends Error { + constructor(message: string, public readonly errorMessageI18nKey: string) { + super(message); + } +} diff --git a/sticker-creator/window/phase3-sticker-functions.ts b/sticker-creator/window/phase3-sticker-functions.ts index 49ed5dd41..beed20bb6 100644 --- a/sticker-creator/window/phase3-sticker-functions.ts +++ b/sticker-creator/window/phase3-sticker-functions.ts @@ -18,6 +18,7 @@ import { initialize as initializeWebAPI } from '../../ts/textsecure/WebAPI'; import { SignalContext } from '../../ts/windows/context'; import { getAnimatedPngDataIfExists } from '../../ts/util/getAnimatedPngDataIfExists'; +import { ProcessStickerImageError } from '../errors'; const STICKER_SIZE = 512; const MIN_STICKER_DIMENSION = 10; @@ -42,12 +43,6 @@ const WebAPI = initializeWebAPI({ version: config.version, }); -function processStickerError(message: string, i18nKey: string): Error { - const result = new Error(message); - result.errorMessageI18nKey = i18nKey; - return result; -} - window.processStickerImage = async (path: string | undefined) => { if (!path) { throw new Error(`Path ${path} is not valid!`); @@ -59,7 +54,7 @@ window.processStickerImage = async (path: string | undefined) => { const { width, height } = meta; if (!width || !height) { - throw processStickerError( + throw new ProcessStickerImageError( 'Sticker height or width were falsy', 'StickerCreator--Toasts--errorProcessing' ); @@ -75,31 +70,31 @@ window.processStickerImage = async (path: string | undefined) => { const animatedPngDataIfExists = getAnimatedPngDataIfExists(imgBuffer); if (animatedPngDataIfExists) { if (imgBuffer.byteLength > MAX_STICKER_BYTE_LENGTH) { - throw processStickerError( + throw new ProcessStickerImageError( 'Sticker file was too large', 'StickerCreator--Toasts--tooLarge' ); } if (width !== height) { - throw processStickerError( + throw new ProcessStickerImageError( 'Sticker must be square', 'StickerCreator--Toasts--APNG--notSquare' ); } if (width > MAX_STICKER_DIMENSION) { - throw processStickerError( + throw new ProcessStickerImageError( 'Sticker dimensions are too large', 'StickerCreator--Toasts--APNG--dimensionsTooLarge' ); } if (width < MIN_STICKER_DIMENSION) { - throw processStickerError( + throw new ProcessStickerImageError( 'Sticker dimensions are too small', 'StickerCreator--Toasts--APNG--dimensionsTooSmall' ); } if (animatedPngDataIfExists.numPlays !== Infinity) { - throw processStickerError( + throw new ProcessStickerImageError( 'Animated stickers must loop forever', 'StickerCreator--Toasts--mustLoopForever' ); @@ -118,7 +113,7 @@ window.processStickerImage = async (path: string | undefined) => { .webp() .toBuffer(); if (processedBuffer.byteLength > MAX_STICKER_BYTE_LENGTH) { - throw processStickerError( + throw new ProcessStickerImageError( 'Sticker file was too large', 'StickerCreator--Toasts--tooLarge' ); diff --git a/ts/ConversationController.ts b/ts/ConversationController.ts index 121683495..0580eccd6 100644 --- a/ts/ConversationController.ts +++ b/ts/ConversationController.ts @@ -299,7 +299,7 @@ export class ConversationController { log.error( 'Contact is not valid. Not saving, but adding to collection:', conversation.idForLogging(), - validationError.stack + Errors.toLogFormat(validationError) ); return conversation; @@ -316,7 +316,7 @@ export class ConversationController { identifier, type, 'Error:', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); throw error; } @@ -1247,7 +1247,7 @@ export class ConversationController { } catch (error) { log.error( 'ConversationController.load/map: Failed to prepare a conversation', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } }) @@ -1256,7 +1256,7 @@ export class ConversationController { } catch (error) { log.error( 'ConversationController: initial fetch failed', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); throw error; } diff --git a/ts/SignalProtocolStore.ts b/ts/SignalProtocolStore.ts index 7df136d6a..c7feaae57 100644 --- a/ts/SignalProtocolStore.ts +++ b/ts/SignalProtocolStore.ts @@ -392,7 +392,7 @@ export class SignalProtocolStore extends EventEmitter { } catch (error) { log.error( 'removePreKey error triggering removePreKey:', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } @@ -604,7 +604,7 @@ export class SignalProtocolStore extends EventEmitter { await this.commitZoneChanges('saveSenderKey'); } } catch (error) { - const errorString = error && error.stack ? error.stack : error; + const errorString = Errors.toLogFormat(error); log.error( `saveSenderKey: failed to save senderKey ${senderId}/${distributionId}: ${errorString}` ); @@ -653,7 +653,7 @@ export class SignalProtocolStore extends EventEmitter { log.info('Successfully fetched sender key(cache miss):', id); return item; } catch (error) { - const errorString = error && error.stack ? error.stack : error; + const errorString = Errors.toLogFormat(error); log.error( `getSenderKey: failed to load sender key ${senderId}/${distributionId}: ${errorString}` ); @@ -679,7 +679,7 @@ export class SignalProtocolStore extends EventEmitter { this.senderKeys.delete(id); } catch (error) { - const errorString = error && error.stack ? error.stack : error; + const errorString = Errors.toLogFormat(error); log.error( `removeSenderKey: failed to remove senderKey ${senderId}/${distributionId}: ${errorString}` ); @@ -860,7 +860,7 @@ export class SignalProtocolStore extends EventEmitter { `pending sender keys size ${this.pendingSenderKeys.size}, ` + `pending sessions size ${this.pendingSessions.size}, ` + `pending unprocessed size ${this.pendingUnprocessed.size}`, - error && error.stack + Errors.toLogFormat(error) ); this.pendingSenderKeys.clear(); this.pendingSessions.clear(); @@ -961,7 +961,7 @@ export class SignalProtocolStore extends EventEmitter { // and save it to the database. return await this._maybeMigrateSession(entry.fromDB, { zone }); } catch (error) { - const errorString = error && error.stack ? error.stack : error; + const errorString = Errors.toLogFormat(error); log.error(`loadSession: failed to load session ${id}: ${errorString}`); return undefined; } @@ -1095,7 +1095,7 @@ export class SignalProtocolStore extends EventEmitter { await this.commitZoneChanges('storeSession'); } } catch (error) { - const errorString = error && error.stack ? error.stack : error; + const errorString = Errors.toLogFormat(error); log.error(`storeSession: Save failed for ${id}: ${errorString}`); throw error; } @@ -1189,7 +1189,7 @@ export class SignalProtocolStore extends EventEmitter { } catch (error) { log.error( 'getOpenDevices: Failed to get devices', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); throw error; } @@ -1692,7 +1692,7 @@ export class SignalProtocolStore extends EventEmitter { } catch (error) { log.error( 'saveIdentity: error triggering keychange:', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } diff --git a/ts/background.ts b/ts/background.ts index 50d7941e4..545b14a18 100644 --- a/ts/background.ts +++ b/ts/background.ts @@ -609,7 +609,7 @@ export async function startApp(): Promise { } catch (error) { log.info( 'User chose not to delete old data. Shutting down.', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); window.shutdown(); return; @@ -627,7 +627,7 @@ export async function startApp(): Promise { } catch (error) { log.error( 'Failed to remove IndexedDB file or remove SQL data:', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } @@ -859,7 +859,7 @@ export async function startApp(): Promise { try { await window.Signal.Data.startInRendererProcess(); } catch (err) { - log.error('SQL failed to initialize', err && err.stack ? err.stack : err); + log.error('SQL failed to initialize', Errors.toLogFormat(err)); } setAppLoadingScreenMessage(window.i18n('loading'), window.i18n); @@ -950,7 +950,7 @@ export async function startApp(): Promise { } catch (error) { log.warn( 'background/setInterval: Failed to parse integer from desktop.retryRespondMaxAge feature flag', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } @@ -961,7 +961,7 @@ export async function startApp(): Promise { } catch (error) { log.error( 'background/onready/setInterval: Error deleting sent protos: ', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } @@ -991,7 +991,7 @@ export async function startApp(): Promise { } catch (error) { log.error( 'background/onready/setInterval: Error getting expired retry placeholders: ', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } }, FIVE_MINUTES); @@ -1038,7 +1038,7 @@ export async function startApp(): Promise { } catch (error) { log.error( 'background.js: ConversationController failed to load:', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } finally { initializeRedux({ mainWindowStats, menuOptions }); @@ -2140,7 +2140,7 @@ export async function startApp(): Promise { } catch (error) { log.error( 'connect: Error refreshing remote config:', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } @@ -2226,7 +2226,7 @@ export async function startApp(): Promise { } catch (e) { log.error( 'Problem with account manager updates after starting new version: ', - e && e.stack ? e.stack : e + Errors.toLogFormat(e) ); } } @@ -2239,7 +2239,7 @@ export async function startApp(): Promise { } catch (error) { log.error( 'Error: Unable to register for unauthenticated delivery support.', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } @@ -2270,7 +2270,7 @@ export async function startApp(): Promise { } catch (error) { log.error( 'Error: Unable to register our capabilities.', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } @@ -2859,7 +2859,10 @@ export async function startApp(): Promise { return; } } catch (error) { - log.error('respondWithProfileKeyBatcher error', error && error.stack); + log.error( + 'respondWithProfileKeyBatcher error', + Errors.toLogFormat(error) + ); } sender.queueJob('sendProfileKeyUpdate', () => @@ -3521,7 +3524,7 @@ export async function startApp(): Promise { log.error( 'unlinkAndDisconnect: Something went wrong clearing ' + 'local configuration', - eraseError && eraseError.stack ? eraseError.stack : eraseError + Errors.toLogFormat(eraseError) ); } finally { window.Signal.Util.Registration.markEverDone(); diff --git a/ts/components/DebugLogWindow.tsx b/ts/components/DebugLogWindow.tsx index def1335f0..9e1d261b0 100644 --- a/ts/components/DebugLogWindow.tsx +++ b/ts/components/DebugLogWindow.tsx @@ -15,6 +15,7 @@ import type { ExecuteMenuRoleType } from './TitleBarContainer'; import { ToastLoadingFullLogs } from './ToastLoadingFullLogs'; import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser'; import { createSupportUrl } from '../util/createSupportUrl'; +import * as Errors from '../types/errors'; import { useEscapeHandling } from '../hooks/useEscapeHandling'; import { useTheme } from '../hooks/useTheme'; @@ -107,10 +108,7 @@ export function DebugLogWindow({ const publishedLogURL = await uploadLogs(text); setPublicLogURL(publishedLogURL); } catch (error) { - log.error( - 'DebugLogWindow error:', - error && error.stack ? error.stack : error - ); + log.error('DebugLogWindow error:', Errors.toLogFormat(error)); setLoadState(LoadState.Loaded); setToastType(ToastType.Error); } diff --git a/ts/components/conversation/GIF.tsx b/ts/components/conversation/GIF.tsx index 6c1c6fee7..5de53e0e0 100644 --- a/ts/components/conversation/GIF.tsx +++ b/ts/components/conversation/GIF.tsx @@ -14,6 +14,7 @@ import { getImageDimensions, defaultBlurHash, } from '../../types/Attachment'; +import * as Errors from '../../types/errors'; import * as log from '../../logging/log'; const MAX_GIF_REPEAT = 4; @@ -90,7 +91,7 @@ export function GIF(props: Props): JSX.Element { video.play().catch(error => { log.info( "Failed to match GIF playback to window's state", - (error && error.stack) || error + Errors.toLogFormat(error) ); }); } else { diff --git a/ts/groups.ts b/ts/groups.ts index afc48a7c5..9996d1cbb 100644 --- a/ts/groups.ts +++ b/ts/groups.ts @@ -453,7 +453,10 @@ async function uploadAvatar( key, }; } catch (error) { - log.warn(`uploadAvatar/${logId} Failed to upload avatar`, error.stack); + log.warn( + `uploadAvatar/${logId} Failed to upload avatar`, + Errors.toLogFormat(error) + ); throw error; } } @@ -2391,7 +2394,7 @@ export async function initiateMigrationToGroupV2( } catch (error) { log.error( `initiateMigrationToGroupV2/${logId}: Error creating group:`, - error.stack + Errors.toLogFormat(error) ); throw error; @@ -2473,7 +2476,7 @@ export async function waitThenRespondToGroupV2Migration( } catch (error) { log.error( `waitThenRespondToGroupV2Migration/${conversation.idForLogging()}: respondToGroupV2Migration failure:`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } }); @@ -2946,7 +2949,7 @@ export async function waitThenMaybeUpdateGroup( } catch (error) { log.error( `waitThenMaybeUpdateGroup/${conversation.idForLogging()}: maybeUpdateGroup failure:`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } }); @@ -2984,7 +2987,7 @@ export async function maybeUpdateGroup( } catch (error) { log.error( `maybeUpdateGroup/${logId}: Failed to update group:`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); throw error; } @@ -3910,7 +3913,7 @@ async function integrateGroupChanges({ } catch (error) { log.error( `integrateGroupChanges/${logId}: Failed to apply change log, continuing to apply remaining change logs.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } @@ -5287,7 +5290,7 @@ export async function applyNewAvatar( } catch (error) { log.warn( `applyNewAvatar/${logId} Failed to handle avatar, clearing it`, - error.stack + Errors.toLogFormat(error) ); if (result.avatar && result.avatar.path) { await window.Signal.Migrations.deleteAttachmentData(result.avatar.path); @@ -5622,7 +5625,7 @@ function decryptGroupChange( } catch (error) { log.warn( `decryptGroupChange/${logId}: Unable to decrypt sourceUuid.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } @@ -5677,7 +5680,7 @@ function decryptGroupChange( } catch (error) { log.warn( `decryptGroupChange/${logId}: Unable to decrypt deleteMembers.deletedUserId. Dropping member.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); return null; } @@ -5711,7 +5714,7 @@ function decryptGroupChange( } catch (error) { log.warn( `decryptGroupChange/${logId}: Unable to decrypt modifyMemberRole.userId. Dropping member.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); return null; } @@ -5844,7 +5847,7 @@ function decryptGroupChange( } catch (error) { log.warn( `decryptGroupChange/${logId}: Unable to decrypt deletePendingMembers.deletedUserId. Dropping member.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); return null; } @@ -6022,7 +6025,7 @@ function decryptGroupChange( } catch (error) { log.warn( `decryptGroupChange/${logId}: Unable to decrypt modifyTitle.title`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } else { @@ -6049,7 +6052,7 @@ function decryptGroupChange( } catch (error) { log.warn( `decryptGroupChange/${logId}: Unable to decrypt modifyDisappearingMessagesTimer.timer`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } else { @@ -6152,7 +6155,7 @@ function decryptGroupChange( } catch (error) { log.warn( `decryptGroupChange/${logId}: Unable to decrypt deletePendingApproval.deletedUserId. Dropping member.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); return null; } @@ -6190,7 +6193,7 @@ function decryptGroupChange( } catch (error) { log.warn( `decryptGroupChange/${logId}: Unable to decrypt promoteAdminApproval.userId. Dropping member.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); return null; } @@ -6232,7 +6235,7 @@ function decryptGroupChange( } catch (error) { log.warn( `decryptGroupChange/${logId}: Unable to decrypt modifyDescription.descriptionBytes`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } else { @@ -6365,7 +6368,7 @@ function decryptGroupState( } catch (error) { log.warn( `decryptGroupState/${logId}: Unable to decrypt title. Clearing it.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } @@ -6388,7 +6391,7 @@ function decryptGroupState( } catch (error) { log.warn( `decryptGroupState/${logId}: Unable to decrypt disappearing message timer. Clearing it.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } @@ -6472,7 +6475,7 @@ function decryptGroupState( } catch (error) { log.warn( `decryptGroupState/${logId}: Unable to decrypt descriptionBytes. Clearing it.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } @@ -6537,7 +6540,7 @@ function decryptMember( } catch (error) { log.warn( `decryptMember/${logId}: Unable to decrypt member userid. Dropping member.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); return undefined; } @@ -6608,7 +6611,7 @@ function decryptMemberPendingProfileKey( } catch (error) { log.warn( `decryptMemberPendingProfileKey/${logId}: Unable to decrypt pending member addedByUserId. Dropping member.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); return undefined; } @@ -6648,7 +6651,7 @@ function decryptMemberPendingProfileKey( } catch (error) { log.warn( `decryptMemberPendingProfileKey/${logId}: Unable to decrypt pending member userId. Dropping member.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); return undefined; } @@ -6673,7 +6676,7 @@ function decryptMemberPendingProfileKey( } catch (error) { log.warn( `decryptMemberPendingProfileKey/${logId}: Unable to decrypt pending member profileKey. Dropping profileKey.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } @@ -6735,7 +6738,7 @@ function decryptMemberPendingAdminApproval( } catch (error) { log.warn( `decryptMemberPendingAdminApproval/${logId}: Unable to decrypt pending member userId. Dropping member.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); return undefined; } @@ -6760,7 +6763,7 @@ function decryptMemberPendingAdminApproval( } catch (error) { log.warn( `decryptMemberPendingAdminApproval/${logId}: Unable to decrypt profileKey. Dropping profileKey.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } diff --git a/ts/logging/main_process_logging.ts b/ts/logging/main_process_logging.ts index 07e5befc2..1b771f968 100644 --- a/ts/logging/main_process_logging.ts +++ b/ts/logging/main_process_logging.ts @@ -19,6 +19,7 @@ import { read as readLastLines } from 'read-last-lines'; import rimraf from 'rimraf'; import type { LoggerType } from '../types/Logging'; +import * as Errors from '../types/errors'; import * as durations from '../util/durations'; import { createRotatingPinoDest } from '../util/rotatingPinoDest'; @@ -67,7 +68,9 @@ export async function initialize( try { await cleanupLogs(logPath); } catch (error) { - const errorString = `Failed to clean logs; deleting all. Error: ${error.stack}`; + const errorString = + 'Failed to clean logs; deleting all. ' + + `Error: ${Errors.toLogFormat(error)}`; console.error(errorString); await deleteAllLogs(logPath); mkdirp.sync(logPath); @@ -136,7 +139,7 @@ export async function initialize( ...rest, }; } catch (error) { - logger.error(`Problem loading log data: ${error.stack}`); + logger.error(`Problem loading log data: ${Errors.toLogFormat(error)}`); return; } @@ -151,7 +154,7 @@ export async function initialize( try { await deleteAllLogs(logPath); } catch (error) { - logger.error(`Problem deleting all logs: ${error.stack}`); + logger.error(`Problem deleting all logs: ${Errors.toLogFormat(error)}`); } }); @@ -196,7 +199,7 @@ async function cleanupLogs(logPath: string) { } catch (error) { console.error( 'Error cleaning logs; deleting and starting over from scratch.', - error.stack + Errors.toLogFormat(error) ); // delete and re-create the log directory @@ -215,7 +218,7 @@ export function isLineAfterDate(line: string, date: Readonly): boolean { const data = JSON.parse(line); return new Date(data.time).getTime() > date.getTime(); } catch (e) { - console.log('error parsing log line', e.stack, line); + console.log('error parsing log line', Errors.toLogFormat(e), line); return false; } } diff --git a/ts/logging/set_up_renderer_logging.ts b/ts/logging/set_up_renderer_logging.ts index fd955e4e7..87f155fde 100644 --- a/ts/logging/set_up_renderer_logging.ts +++ b/ts/logging/set_up_renderer_logging.ts @@ -22,6 +22,7 @@ import { } from './shared'; import * as log from './log'; import { Environment, getEnvironment } from '../environment'; +import * as Errors from '../types/errors'; import { createRotatingPinoDest } from '../util/rotatingPinoDest'; // Backwards-compatible logging, simple strings and no level (defaulted to INFO) @@ -114,14 +115,13 @@ window.SignalContext.log = { }; window.onerror = (_message, _script, _line, _col, error) => { - const errorInfo = error && error.stack ? error.stack : JSON.stringify(error); + const errorInfo = Errors.toLogFormat(error); log.error(`Top-level unhandled error: ${errorInfo}`); }; window.addEventListener('unhandledrejection', rejectionEvent => { const error = rejectionEvent.reason; - const errorString = - error && error.stack ? error.stack : JSON.stringify(error); + const errorString = Errors.toLogFormat(error); log.error(`Top-level unhandled promise rejection: ${errorString}`); }); diff --git a/ts/messageModifiers/Deletes.ts b/ts/messageModifiers/Deletes.ts index 439c4e069..0e4b1e5aa 100644 --- a/ts/messageModifiers/Deletes.ts +++ b/ts/messageModifiers/Deletes.ts @@ -7,6 +7,7 @@ import { Collection, Model } from 'backbone'; import type { MessageModel } from '../models/messages'; import { getContactId } from '../messages/helpers'; import * as log from '../logging/log'; +import * as Errors from '../types/errors'; import { deleteForEveryone } from '../util/deleteForEveryone'; export type DeleteAttributesType = { @@ -97,10 +98,7 @@ export class Deletes extends Collection { this.remove(del); }); } catch (error) { - log.error( - 'Deletes.onDelete error:', - error && error.stack ? error.stack : error - ); + log.error('Deletes.onDelete error:', Errors.toLogFormat(error)); } } } diff --git a/ts/messageModifiers/MessageReceipts.ts b/ts/messageModifiers/MessageReceipts.ts index 0f36f6405..bc9ecbf3b 100644 --- a/ts/messageModifiers/MessageReceipts.ts +++ b/ts/messageModifiers/MessageReceipts.ts @@ -15,6 +15,7 @@ import { getOwn } from '../util/getOwn'; import { missingCaseError } from '../util/missingCaseError'; import { createWaitBatcher } from '../util/waitBatcher'; import type { UUIDStringType } from '../types/UUID'; +import * as Errors from '../types/errors'; import { SendActionType, SendStatus, @@ -331,10 +332,7 @@ export class MessageReceipts extends Collection { this.remove(receipt); } catch (error) { - log.error( - 'MessageReceipts.onReceipt error:', - error && error.stack ? error.stack : error - ); + log.error('MessageReceipts.onReceipt error:', Errors.toLogFormat(error)); } } } diff --git a/ts/messageModifiers/MessageRequests.ts b/ts/messageModifiers/MessageRequests.ts index 7a9f5ca1f..b26179094 100644 --- a/ts/messageModifiers/MessageRequests.ts +++ b/ts/messageModifiers/MessageRequests.ts @@ -6,6 +6,7 @@ import { Collection, Model } from 'backbone'; import type { ConversationModel } from '../models/conversations'; import * as log from '../logging/log'; +import * as Errors from '../types/errors'; export type MessageRequestAttributesType = { threadE164?: string; @@ -122,10 +123,7 @@ export class MessageRequests extends Collection { this.remove(sync); } catch (error) { - log.error( - 'MessageRequests.onResponse error:', - error && error.stack ? error.stack : error - ); + log.error('MessageRequests.onResponse error:', Errors.toLogFormat(error)); } } } diff --git a/ts/messageModifiers/Reactions.ts b/ts/messageModifiers/Reactions.ts index c45b21991..7bb6d5b2c 100644 --- a/ts/messageModifiers/Reactions.ts +++ b/ts/messageModifiers/Reactions.ts @@ -10,6 +10,7 @@ import type { MessageAttributesType, ReactionAttributesType, } from '../model-types.d'; +import * as Errors from '../types/errors'; import * as log from '../logging/log'; import { getContactId, getContact } from '../messages/helpers'; import { isDirectConversation, isMe } from '../util/whatTypeOfConversation'; @@ -213,10 +214,7 @@ export class Reactions extends Collection { this.remove(reaction); }); } catch (error) { - log.error( - 'Reactions.onReaction error:', - error && error.stack ? error.stack : error - ); + log.error('Reactions.onReaction error:', Errors.toLogFormat(error)); } } } diff --git a/ts/messageModifiers/ReadSyncs.ts b/ts/messageModifiers/ReadSyncs.ts index b98229b7a..58775e12f 100644 --- a/ts/messageModifiers/ReadSyncs.ts +++ b/ts/messageModifiers/ReadSyncs.ts @@ -10,6 +10,7 @@ import { isIncoming } from '../state/selectors/message'; import { isMessageUnread } from '../util/isMessageUnread'; import { notificationService } from '../services/notifications'; import * as log from '../logging/log'; +import * as Errors from '../types/errors'; export type ReadSyncAttributesType = { senderId: string; @@ -142,10 +143,7 @@ export class ReadSyncs extends Collection { this.remove(sync); } catch (error) { - log.error( - 'ReadSyncs.onSync error:', - error && error.stack ? error.stack : error - ); + log.error('ReadSyncs.onSync error:', Errors.toLogFormat(error)); } } } diff --git a/ts/messageModifiers/ViewOnceOpenSyncs.ts b/ts/messageModifiers/ViewOnceOpenSyncs.ts index 27764f956..c8d05fe26 100644 --- a/ts/messageModifiers/ViewOnceOpenSyncs.ts +++ b/ts/messageModifiers/ViewOnceOpenSyncs.ts @@ -6,6 +6,7 @@ import { Collection, Model } from 'backbone'; import type { MessageModel } from '../models/messages'; import * as log from '../logging/log'; +import * as Errors from '../types/errors'; export type ViewOnceOpenSyncAttributesType = { source?: string; @@ -94,10 +95,7 @@ export class ViewOnceOpenSyncs extends Collection { this.remove(sync); } catch (error) { - log.error( - 'ViewOnceOpenSyncs.onSync error:', - error && error.stack ? error.stack : error - ); + log.error('ViewOnceOpenSyncs.onSync error:', Errors.toLogFormat(error)); } } } diff --git a/ts/messageModifiers/ViewSyncs.ts b/ts/messageModifiers/ViewSyncs.ts index 34555823d..5743232b9 100644 --- a/ts/messageModifiers/ViewSyncs.ts +++ b/ts/messageModifiers/ViewSyncs.ts @@ -9,6 +9,7 @@ import type { MessageModel } from '../models/messages'; import { ReadStatus } from '../messages/MessageReadStatus'; import { markViewed } from '../services/MessageUpdater'; import { isDownloaded } from '../types/Attachment'; +import * as Errors from '../types/errors'; import { isIncoming } from '../state/selectors/message'; import { notificationService } from '../services/notifications'; import { queueAttachmentDownloads } from '../util/queueAttachmentDownloads'; @@ -111,10 +112,7 @@ export class ViewSyncs extends Collection { this.remove(sync); } catch (error) { - log.error( - 'ViewSyncs.onSync error:', - error && error.stack ? error.stack : error - ); + log.error('ViewSyncs.onSync error:', Errors.toLogFormat(error)); } } } diff --git a/ts/models/messages.ts b/ts/models/messages.ts index 5f461d2e7..3c83f61cf 100644 --- a/ts/models/messages.ts +++ b/ts/models/messages.ts @@ -1242,7 +1242,7 @@ export class MessageModel extends window.Backbone.Model { } catch (error) { log.error( `Error erasing data for message ${this.idForLogging()}:`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } @@ -1358,11 +1358,7 @@ export class MessageModel extends window.Backbone.Model { } errors.forEach(e => { - log.error( - 'Message.saveErrors:', - e && e.reason ? e.reason : null, - e && e.stack ? e.stack : e - ); + log.error('Message.saveErrors:', Errors.toLogFormat(e)); }); errors = errors.map(e => { // Note: in our environment, instanceof can be scary, so we have a backup check @@ -2415,7 +2411,7 @@ export class MessageModel extends window.Backbone.Model { sentAt: message.get('sent_at'), }); } catch (error) { - const errorText = error && error.stack ? error.stack : error; + const errorText = Errors.toLogFormat(error); log.error( `${idLog}: Failed to process group update as part of message ${message.idForLogging()}: ${errorText}` ); @@ -3050,7 +3046,7 @@ export class MessageModel extends window.Backbone.Model { log.info(`${idLog}: Batching save`); this.saveAndNotify(conversation, confirm); } catch (error) { - const errorForLog = error && error.stack ? error.stack : error; + const errorForLog = Errors.toLogFormat(error); log.error(`${idLog}: error:`, errorForLog); throw error; } diff --git a/ts/routineProfileRefresh.ts b/ts/routineProfileRefresh.ts index 07073b1dd..1063ff007 100644 --- a/ts/routineProfileRefresh.ts +++ b/ts/routineProfileRefresh.ts @@ -144,7 +144,7 @@ export async function routineProfileRefresh({ } catch (err) { log.error( `${logId}: refreshed profile for ${conversation.idForLogging()}`, - err?.stack || err + Errors.toLogFormat(err) ); } } diff --git a/ts/services/LinkPreview.ts b/ts/services/LinkPreview.ts index 3b56c2557..c1836f5fb 100644 --- a/ts/services/LinkPreview.ts +++ b/ts/services/LinkPreview.ts @@ -11,6 +11,7 @@ import type { MaybeGrabLinkPreviewOptionsType, AddLinkPreviewOptionsType, } from '../types/LinkPreview'; +import * as Errors from '../types/errors'; import type { StickerPackType as StickerPackDBType } from '../sql/Interface'; import type { MIMEType } from '../types/MIME'; import * as Bytes from '../Bytes'; @@ -216,7 +217,7 @@ export async function addLinkPreview( } catch (error) { log.error( 'Problem loading link preview, disabling.', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); disableLinkPreviews = true; removeLinkPreview(); @@ -455,10 +456,7 @@ async function getStickerPackPreview( url, }; } catch (error) { - log.error( - 'getStickerPackPreview error:', - error && error.stack ? error.stack : error - ); + log.error('getStickerPackPreview error:', Errors.toLogFormat(error)); return null; } finally { if (id) { @@ -530,7 +528,7 @@ async function getGroupPreview( ), }; } catch (error) { - const errorString = error && error.stack ? error.stack : error; + const errorString = Errors.toLogFormat(error); log.error( `getGroupPreview/${logId}: Failed to fetch avatar ${errorString}` ); diff --git a/ts/services/audioRecorder.ts b/ts/services/audioRecorder.ts index 446c5305e..3f2c735a9 100644 --- a/ts/services/audioRecorder.ts +++ b/ts/services/audioRecorder.ts @@ -4,6 +4,7 @@ import { requestMicrophonePermissions } from '../util/requestMicrophonePermissions'; import * as log from '../logging/log'; import type { WebAudioRecorderClass } from '../window.d'; +import * as Errors from '../types/errors'; export class RecorderClass { private context?: AudioContext; @@ -84,10 +85,7 @@ export class RecorderClass { this.source.connect(this.input); this.stream = stream; } catch (err) { - log.error( - 'Recorder.onGetUserMediaError:', - err && err.stack ? err.stack : err - ); + log.error('Recorder.onGetUserMediaError:', Errors.toLogFormat(err)); this.clear(); throw err; } @@ -135,7 +133,7 @@ export class RecorderClass { this.clear(); - log.error('Recorder/onError:', error && error.stack ? error.stack : error); + log.error('Recorder/onError:', Errors.toLogFormat(error)); } getBlob(): Blob { diff --git a/ts/services/calling.ts b/ts/services/calling.ts index 34ca0a118..eacf37084 100644 --- a/ts/services/calling.ts +++ b/ts/services/calling.ts @@ -67,6 +67,7 @@ import { } from '../calling/findBestMatchingDevice'; import type { LocalizerType } from '../types/Util'; import { UUID, UUIDKind } from '../types/UUID'; +import * as Errors from '../types/errors'; import type { ConversationModel } from '../models/conversations'; import * as Bytes from '../Bytes'; import { uuidToBytes, bytesToUuid } from '../Crypto'; @@ -1061,10 +1062,7 @@ export class CallingClass { sendType: 'callingMessage', timestamp, }).catch(err => { - log.error( - 'Failed to send group call update:', - err && err.stack ? err.stack : err - ); + log.error('Failed to send group call update:', Errors.toLogFormat(err)); }); } @@ -1877,7 +1875,7 @@ export class CallingClass { return await this.getCallSettings(conversation); } catch (err) { - log.error(`Ignoring incoming call: ${err.stack}`); + log.error(`Ignoring incoming call: ${Errors.toLogFormat(err)}`); this.addCallHistoryForFailedIncomingCall( conversation, call.isVideoCall, diff --git a/ts/services/expiringMessagesDeletion.ts b/ts/services/expiringMessagesDeletion.ts index 8d33b0db9..0859a811d 100644 --- a/ts/services/expiringMessagesDeletion.ts +++ b/ts/services/expiringMessagesDeletion.ts @@ -7,6 +7,7 @@ import type { MessageModel } from '../models/messages'; import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary'; import { sleep } from '../util/sleep'; import { SECOND } from '../util/durations'; +import * as Errors from '../types/errors'; class ExpiringMessagesDeletionService { public update: typeof this.checkExpiringMessages; @@ -65,7 +66,7 @@ class ExpiringMessagesDeletionService { } catch (error) { window.SignalContext.log.error( 'destroyExpiredMessages: Error deleting expired messages', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); window.SignalContext.log.info( 'destroyExpiredMessages: Waiting 30 seconds before trying again' diff --git a/ts/services/senderCertificate.ts b/ts/services/senderCertificate.ts index 1657b036b..5c1b9e0e5 100644 --- a/ts/services/senderCertificate.ts +++ b/ts/services/senderCertificate.ts @@ -12,6 +12,7 @@ import { missingCaseError } from '../util/missingCaseError'; import { waitForOnline } from '../util/waitForOnline'; import * as log from '../logging/log'; import type { StorageInterface } from '../types/Storage.d'; +import * as Errors from '../types/errors'; import type { WebAPIType } from '../textsecure/WebAPI'; import { SignalService as Proto } from '../protobuf'; @@ -171,7 +172,7 @@ export class SenderCertificateService { `Sender certificate service could not fetch a ${modeToLogString( mode )} certificate. Returning undefined`, - err && err.stack ? err.stack : err + Errors.toLogFormat(err) ); return undefined; } diff --git a/ts/services/tapToViewMessagesDeletionService.ts b/ts/services/tapToViewMessagesDeletionService.ts index 6e147c34b..fdfd05aac 100644 --- a/ts/services/tapToViewMessagesDeletionService.ts +++ b/ts/services/tapToViewMessagesDeletionService.ts @@ -4,6 +4,7 @@ import { debounce } from 'lodash'; import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary'; import { DAY } from '../util/durations'; +import * as Errors from '../types/errors'; async function eraseTapToViewMessages() { try { @@ -30,7 +31,7 @@ async function eraseTapToViewMessages() { } catch (error) { window.SignalContext.log.error( 'eraseTapToViewMessages: Error erasing messages', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } diff --git a/ts/shims/deleteAllData.ts b/ts/shims/deleteAllData.ts index e237d2ea3..45ee2a0b2 100644 --- a/ts/shims/deleteAllData.ts +++ b/ts/shims/deleteAllData.ts @@ -3,6 +3,7 @@ import * as log from '../logging/log'; import { deleteAllLogs } from '../util/deleteAllLogs'; +import * as Errors from '../types/errors'; export async function deleteAllData(): Promise { try { @@ -28,7 +29,7 @@ export async function deleteAllData(): Promise { } catch (error) { log.error( 'Something went wrong deleting all data:', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } window.restart(); diff --git a/ts/sql/mainWorker.ts b/ts/sql/mainWorker.ts index 1668803c3..0af55cec5 100644 --- a/ts/sql/mainWorker.ts +++ b/ts/sql/mainWorker.ts @@ -4,6 +4,7 @@ import { parentPort } from 'worker_threads'; import type { LoggerType } from '../types/Logging'; +import * as Errors from '../types/errors'; import type { WrappedWorkerRequest, WrappedWorkerResponse, @@ -22,7 +23,7 @@ function respond(seq: number, error: Error | undefined, response?: any) { const wrappedResponse: WrappedWorkerResponse = { type: 'response', seq, - error: error?.stack, + error: error === undefined ? undefined : Errors.toLogFormat(error), response, }; port.postMessage(wrappedResponse); diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index efec22446..9318e77fb 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -55,6 +55,7 @@ import { CallMode } from '../../types/Calling'; import type { MediaItemType } from '../../types/MediaItem'; import type { UUIDStringType } from '../../types/UUID'; import { MY_STORY_ID, StorySendMode } from '../../types/Stories'; +import * as Errors from '../../types/errors'; import { getGroupSizeRecommendedLimit, getGroupSizeHardLimit, @@ -1206,7 +1207,7 @@ function myProfileChanged( payload: null, }); } catch (err) { - log.error('myProfileChanged', err && err.stack ? err.stack : err); + log.error('myProfileChanged', Errors.toLogFormat(err)); dispatch({ type: TOGGLE_PROFILE_EDITOR_ERROR }); } }; @@ -1606,7 +1607,7 @@ function createGroup( }) ); } catch (err) { - log.error('Failed to create group', err && err.stack ? err.stack : err); + log.error('Failed to create group', Errors.toLogFormat(err)); dispatch({ type: 'CREATE_GROUP_REJECTED' }); } }; diff --git a/ts/state/ducks/safetyNumber.ts b/ts/state/ducks/safetyNumber.ts index af01a393e..f0f6f8dd2 100644 --- a/ts/state/ducks/safetyNumber.ts +++ b/ts/state/ducks/safetyNumber.ts @@ -8,6 +8,7 @@ import { toggleVerification, } from '../../shims/contactVerification'; import * as log from '../../logging/log'; +import * as Errors from '../../types/errors'; export type SafetyNumberContactType = { safetyNumber: string; @@ -108,10 +109,7 @@ async function alterVerification(contact: ConversationType): Promise { if (result.name === 'OutgoingIdentityKeyError') { throw result; } else { - log.error( - 'failed to toggle verified:', - result && result.stack ? result.stack : result - ); + log.error('failed to toggle verified:', Errors.toLogFormat(result)); } } else { const keyError = result.errors.find( @@ -121,10 +119,7 @@ async function alterVerification(contact: ConversationType): Promise { throw keyError; } else { result.errors.forEach((error: Error) => { - log.error( - 'failed to toggle verified:', - error && error.stack ? error.stack : error - ); + log.error('failed to toggle verified:', Errors.toLogFormat(error)); }); } } diff --git a/ts/state/smart/ForwardMessageModal.tsx b/ts/state/smart/ForwardMessageModal.tsx index 2491242dd..d3d4035d5 100644 --- a/ts/state/smart/ForwardMessageModal.tsx +++ b/ts/state/smart/ForwardMessageModal.tsx @@ -9,6 +9,7 @@ import type { StateType } from '../reducer'; import * as log from '../../logging/log'; import { ForwardMessageModal } from '../../components/ForwardMessageModal'; import { LinkPreviewSourceType } from '../../types/LinkPreview'; +import * as Errors from '../../types/errors'; import type { GetConversationByIdType } from '../selectors/conversations'; import { getAllComposableConversations, @@ -107,7 +108,7 @@ export function SmartForwardMessageModal(): JSX.Element | null { closeModal(); } } catch (err) { - log.warn('doForwardMessage', err && err.stack ? err.stack : err); + log.warn('doForwardMessage', Errors.toLogFormat(err)); } }} getPreferredBadge={getPreferredBadge} diff --git a/ts/test-electron/util/sendToGroup_test.ts b/ts/test-electron/util/sendToGroup_test.ts index 01dcfb6ef..83dbd1a10 100644 --- a/ts/test-electron/util/sendToGroup_test.ts +++ b/ts/test-electron/util/sendToGroup_test.ts @@ -3,6 +3,7 @@ import { assert } from 'chai'; import * as sinon from 'sinon'; +import { LibSignalErrorBase } from '@signalapp/libsignal-client'; import { _analyzeSenderKeyDevices, @@ -172,7 +173,11 @@ describe('sendToGroup', () => { }); it("returns true for any error with 'untrusted' identity", async () => { - const error = new Error('This was an untrusted identity.'); + const error = new LibSignalErrorBase( + 'untrusted identity', + 'UntrustedIdentity', + 'ignored' + ); assert.isTrue(_shouldFailSend(error, 'logId')); }); diff --git a/ts/textsecure/AccountManager.ts b/ts/textsecure/AccountManager.ts index c515c82d6..d4bca9006 100644 --- a/ts/textsecure/AccountManager.ts +++ b/ts/textsecure/AccountManager.ts @@ -575,7 +575,7 @@ export default class AccountManager extends EventTarget { } catch (error) { log.error( 'Something went wrong deleting data from previous number', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } else { diff --git a/ts/textsecure/ContactsParser.ts b/ts/textsecure/ContactsParser.ts index d03485efd..a73472bb8 100644 --- a/ts/textsecure/ContactsParser.ts +++ b/ts/textsecure/ContactsParser.ts @@ -8,6 +8,7 @@ import protobuf from '../protobuf/wrap'; import { SignalService as Proto } from '../protobuf'; import { normalizeUuid } from '../util/normalizeUuid'; import { DurationInSeconds } from '../util/durations'; +import * as Errors from '../types/errors'; import * as log from '../logging/log'; import Avatar = Proto.ContactDetails.IAvatar; @@ -90,10 +91,7 @@ abstract class ParserBase< expireTimer, }; } catch (error) { - log.error( - 'ProtoParser.next error:', - error && error.stack ? error.stack : error - ); + log.error('ProtoParser.next error:', Errors.toLogFormat(error)); return undefined; } } diff --git a/ts/textsecure/Errors.ts b/ts/textsecure/Errors.ts index 7ae40acb9..e7f63db4b 100644 --- a/ts/textsecure/Errors.ts +++ b/ts/textsecure/Errors.ts @@ -54,8 +54,9 @@ export class ReplayableError extends Error { name?: string; message: string; functionCode?: number; + cause?: unknown; }) { - super(options.message); + super(options.message, { cause: options.cause }); this.name = options.name || 'ReplayableError'; this.message = options.message; @@ -71,7 +72,7 @@ export class ReplayableError extends Error { } export class OutgoingIdentityKeyError extends ReplayableError { - identifier: string; + public readonly identifier: string; // Note: Data to resend message is no longer captured constructor(incomingIdentifier: string) { @@ -162,6 +163,7 @@ export class SendMessageChallengeError extends ReplayableError { super({ name: 'SendMessageChallengeError', message: httpError.message, + cause: httpError, }); [this.identifier] = identifier.split('.'); @@ -237,9 +239,7 @@ export class SendMessageProtoError extends Error implements CallbackResultType { return 'No errors'; } - return errors - .map(error => (error.stackForLog ? error.stackForLog : error.toString())) - .join(', '); + return errors.map(error => error.toString()).join(', '); } } diff --git a/ts/textsecure/OutgoingMessage.ts b/ts/textsecure/OutgoingMessage.ts index 2f5922f63..94343eb3b 100644 --- a/ts/textsecure/OutgoingMessage.ts +++ b/ts/textsecure/OutgoingMessage.ts @@ -13,6 +13,8 @@ import type { PlaintextContent, } from '@signalapp/libsignal-client'; import { + ErrorCode, + LibSignalErrorBase, CiphertextMessageType, ProtocolAddress, sealedSenderEncrypt, @@ -34,6 +36,7 @@ import { import type { CallbackResultType, CustomError } from './Types.d'; import { isValidNumber } from '../types/PhoneNumber'; import { Address } from '../types/Address'; +import * as Errors from '../types/errors'; import { QualifiedAddress } from '../types/QualifiedAddress'; import { UUID, isValidUuid } from '../types/UUID'; import { Sessions, IdentityKeys } from '../LibSignalStores'; @@ -244,8 +247,7 @@ export default class OutgoingMessage { } } - error.reason = reason; - error.stackForLog = providedError ? providedError.stack : undefined; + error.cause = reason; this.errors[this.errors.length] = error; this.numberCompleted(); @@ -284,21 +286,14 @@ export default class OutgoingMessage { : { accessKey: undefined }; const { accessKey } = info; - try { - const { accessKeyFailed } = await getKeysForIdentifier( - identifier, - this.server, - updateDevices, - accessKey - ); - if (accessKeyFailed && !this.failoverIdentifiers.includes(identifier)) { - this.failoverIdentifiers.push(identifier); - } - } catch (error) { - if (error?.message?.includes('untrusted identity for address')) { - error.timestamp = this.timestamp; - } - throw error; + const { accessKeyFailed } = await getKeysForIdentifier( + identifier, + this.server, + updateDevices, + accessKey + ); + if (accessKeyFailed && !this.failoverIdentifiers.includes(identifier)) { + this.failoverIdentifiers.push(identifier); } } @@ -626,8 +621,13 @@ export default class OutgoingMessage { ); }); } - if (error?.message?.includes('untrusted identity for address')) { - error.timestamp = this.timestamp; + + let newError = error; + if ( + error instanceof LibSignalErrorBase && + error.code === ErrorCode.UntrustedIdentity + ) { + newError = new OutgoingIdentityKeyError(identifier); log.error( 'Got "key changed" error from encrypt - no identityKey for application layer', identifier, @@ -643,7 +643,8 @@ export default class OutgoingMessage { }, innerError => { log.error( - `doSendMessage: Error closing sessions: ${innerError.stack}` + 'doSendMessage: Error closing sessions: ' + + `${Errors.toLogFormat(innerError)}` ); throw error; } @@ -653,7 +654,7 @@ export default class OutgoingMessage { this.registerError( identifier, 'Failed to create or send message', - error + newError ); return undefined; @@ -712,7 +713,7 @@ export default class OutgoingMessage { } catch (error) { log.error( `sendToIdentifier: Failed to fetch UUID for identifier ${identifier}`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } else { @@ -731,7 +732,10 @@ export default class OutgoingMessage { } await this.reloadDevicesAndSend(identifier, true)(); } catch (error) { - if (error?.message?.includes('untrusted identity for address')) { + if ( + error instanceof LibSignalErrorBase && + error.code === ErrorCode.UntrustedIdentity + ) { const newError = new OutgoingIdentityKeyError(identifier); this.registerError(identifier, 'Untrusted identity', newError); } else { diff --git a/ts/textsecure/getKeysForIdentifier.ts b/ts/textsecure/getKeysForIdentifier.ts index b5e72c918..a808dee56 100644 --- a/ts/textsecure/getKeysForIdentifier.ts +++ b/ts/textsecure/getKeysForIdentifier.ts @@ -2,6 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import { + ErrorCode, + LibSignalErrorBase, PreKeyBundle, processPreKeyBundle, ProtocolAddress, @@ -9,9 +11,9 @@ import { } from '@signalapp/libsignal-client'; import { + OutgoingIdentityKeyError, UnregisteredUserError, HTTPError, - OutgoingIdentityKeyError, } from './Errors'; import { Sessions, IdentityKeys } from '../LibSignalStores'; import { Address } from '../types/Address'; @@ -72,13 +74,6 @@ async function getServerKeys( }), }; } catch (error: unknown) { - if ( - error instanceof Error && - error.message.includes('untrusted identity') - ) { - throw new OutgoingIdentityKeyError(identifier); - } - if ( accessKey && isRecord(error) && @@ -155,22 +150,27 @@ async function handleServerKeys( ourUuid, new Address(theirUuid, deviceId) ); - await window.textsecure.storage.protocol - .enqueueSessionJob(address, () => - processPreKeyBundle( - preKeyBundle, - protocolAddress, - sessionStore, - identityKeyStore - ) - ) - .catch(error => { - if (error?.message?.includes('untrusted identity for address')) { - // eslint-disable-next-line no-param-reassign - error.identityKey = response.identityKey; - } - throw error; - }); + + try { + await window.textsecure.storage.protocol.enqueueSessionJob( + address, + () => + processPreKeyBundle( + preKeyBundle, + protocolAddress, + sessionStore, + identityKeyStore + ) + ); + } catch (error) { + if ( + error instanceof LibSignalErrorBase && + error.code === ErrorCode.UntrustedIdentity + ) { + throw new OutgoingIdentityKeyError(identifier); + } + throw error; + } }) ); } diff --git a/ts/types/Stickers.ts b/ts/types/Stickers.ts index 0f609afb0..dfd3f9f3f 100644 --- a/ts/types/Stickers.ts +++ b/ts/types/Stickers.ts @@ -529,7 +529,7 @@ export async function downloadEphemeralPack( } log.error( `Ephemeral download error for sticker pack ${redactPackId(packId)}:`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } @@ -554,7 +554,7 @@ export async function downloadStickerPack( } catch (error) { log.error( 'doDownloadStickerPack threw an error:', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } }); @@ -696,7 +696,7 @@ async function doDownloadStickerPack( } catch (error) { log.error( `Error downloading manifest for sticker pack ${redactPackId(packId)}:`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); const pack = { @@ -779,7 +779,7 @@ async function doDownloadStickerPack( } catch (error) { log.error( `Error downloading stickers for sticker pack ${redactPackId(packId)}:`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); const errorStatus = 'error'; diff --git a/ts/types/errors.ts b/ts/types/errors.ts index fd0ceeea1..7b65721a1 100644 --- a/ts/types/errors.ts +++ b/ts/types/errors.ts @@ -4,15 +4,20 @@ import { get, has } from 'lodash'; export function toLogFormat(error: unknown): string { + let result = ''; if (error instanceof Error && error.stack) { - return error.stack; + result = error.stack; + } else if (has(error, 'message')) { + result = get(error, 'message'); + } else { + result = String(error); } - if (has(error, 'message')) { - return get(error, 'message'); + if (has(error, 'cause')) { + result += `\nCaused by: ${String(get(error, 'cause'))}`; } - return String(error); + return result; } export class ProfileDecryptError extends Error {} diff --git a/ts/util/assert.ts b/ts/util/assert.ts index b58c2f0ac..c4549be62 100644 --- a/ts/util/assert.ts +++ b/ts/util/assert.ts @@ -3,6 +3,7 @@ import { getEnvironment, Environment } from '../environment'; import * as log from '../logging/log'; +import * as Errors from '../types/errors'; /** * In production and beta, logs a warning and continues. For development it @@ -15,7 +16,7 @@ export function softAssert(condition: unknown, message: string): void { } const err = new Error(message); - log.warn('softAssert failure:', err && err.stack ? err.stack : err); + log.warn('softAssert failure:', Errors.toLogFormat(err)); } } @@ -34,7 +35,7 @@ export function assertDev( } throw err; } - log.error('assert failure:', err && err.stack ? err.stack : err); + log.error('assert failure:', Errors.toLogFormat(err)); } } diff --git a/ts/util/attachments.ts b/ts/util/attachments.ts index 8cf3492a9..140b07b9e 100644 --- a/ts/util/attachments.ts +++ b/ts/util/attachments.ts @@ -8,11 +8,11 @@ import { getValue } from '../RemoteConfig'; import { parseIntOrThrow } from './parseIntOrThrow'; import { scaleImageToLevel } from './scaleImageToLevel'; -import { isRecord } from './isRecord'; import type { AttachmentType } from '../types/Attachment'; import { canBeTranscoded } from '../types/Attachment'; import type { LoggerType } from '../types/Logging'; import * as MIME from '../types/MIME'; +import * as Errors from '../types/errors'; const MEBIBYTE = 1024 * 1024; const DEFAULT_MAX = 100 * MEBIBYTE; @@ -87,8 +87,7 @@ export async function autoOrientJPEG( return xcodedAttachment; } catch (error: unknown) { - const errorString = - isRecord(error) && 'stack' in error ? error.stack : error; + const errorString = Errors.toLogFormat(error); logger.error( 'autoOrientJPEG: Failed to rotate/scale attachment', errorString diff --git a/ts/util/createIPCEvents.tsx b/ts/util/createIPCEvents.tsx index 5747c056d..ce63d8ba7 100644 --- a/ts/util/createIPCEvents.tsx +++ b/ts/util/createIPCEvents.tsx @@ -14,6 +14,7 @@ import type { DefaultConversationColorType, } from '../types/Colors'; import { DEFAULT_CONVERSATION_COLOR } from '../types/Colors'; +import * as Errors from '../types/errors'; import * as Stickers from '../types/Stickers'; import type { SystemTraySetting } from '../types/SystemTraySetting'; import { parseSystemTraySetting } from '../types/SystemTraySetting'; @@ -453,7 +454,7 @@ export function createIPCEvents( window.isShowingModal = false; log.error( 'showStickerPack: Ran into an error!', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); const errorView = new ReactWrapperView({ className: 'error-modal-wrapper', @@ -483,7 +484,7 @@ export function createIPCEvents( } catch (error) { log.error( 'showGroupViaLink: Ran into an error!', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); const errorView = new ReactWrapperView({ className: 'error-modal-wrapper', diff --git a/ts/util/handleRetry.ts b/ts/util/handleRetry.ts index 42b81542a..b80506262 100644 --- a/ts/util/handleRetry.ts +++ b/ts/util/handleRetry.ts @@ -102,7 +102,7 @@ export async function onRetryRequest(event: RetryRequestEvent): Promise { } catch (error) { log.warn( `onRetryRequest/${logId}: Failed to parse integer from desktop.retryRespondMaxAge feature flag`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } @@ -349,7 +349,7 @@ async function sendDistributionMessageOrNullMessage( } catch (error) { log.error( `sendDistributionMessageOrNullMessage/${logId}: Failed to send sender key distribution message`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } @@ -651,7 +651,7 @@ async function requestResend(decryptionError: DecryptionErrorEventData) { } catch (error) { log.error( `requestResend/${logId}: Failed to send retry request, failing over to automatic reset`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); startAutomaticSessionReset(decryptionError); return; diff --git a/ts/util/hasExpired.ts b/ts/util/hasExpired.ts index f13bf4435..63434b579 100644 --- a/ts/util/hasExpired.ts +++ b/ts/util/hasExpired.ts @@ -4,6 +4,7 @@ import { Environment, getEnvironment } from '../environment'; import { isInPast } from './timestamp'; import * as log from '../logging/log'; +import * as Errors from '../types/errors'; const ONE_DAY_MS = 86400 * 1000; const NINETY_ONE_DAYS = 91 * ONE_DAY_MS; @@ -18,7 +19,7 @@ export function hasExpired(): boolean { log.info('Build expires: ', new Date(buildExpiration).toISOString()); } } catch (e) { - log.error('Error retrieving build expiration date', e.stack); + log.error('Error retrieving build expiration date', Errors.toLogFormat(e)); return true; } diff --git a/ts/util/imageToBlurHash.ts b/ts/util/imageToBlurHash.ts index 76b6967ff..17bbb1c97 100644 --- a/ts/util/imageToBlurHash.ts +++ b/ts/util/imageToBlurHash.ts @@ -13,9 +13,9 @@ const loadImageData = async (input: Input): Promise => { canvasOrError => { if (canvasOrError instanceof Event && canvasOrError.type === 'error') { const processError = new Error( - 'imageToBlurHash: Failed to process image' + 'imageToBlurHash: Failed to process image', + { cause: canvasOrError } ); - processError.originalError = canvasOrError; reject(processError); return; } diff --git a/ts/util/longRunningTaskWrapper.tsx b/ts/util/longRunningTaskWrapper.tsx index 4c817330a..2006ced72 100644 --- a/ts/util/longRunningTaskWrapper.tsx +++ b/ts/util/longRunningTaskWrapper.tsx @@ -6,6 +6,7 @@ import { ReactWrapperView } from '../views/ReactWrapperView'; import { ErrorModal } from '../components/ErrorModal'; import { ProgressModal } from '../components/ProgressModal'; import * as log from '../logging/log'; +import * as Errors from '../types/errors'; import { clearTimeoutIfNecessary } from './clearTimeoutIfNecessary'; export async function longRunningTaskWrapper({ @@ -62,7 +63,7 @@ export async function longRunningTaskWrapper({ } catch (error) { log.error( `longRunningTaskWrapper/${idLog}: Error!`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); clearTimeoutIfNecessary(progressTimeout); diff --git a/ts/util/preload.ts b/ts/util/preload.ts index 82b836705..a5ee89ce0 100644 --- a/ts/util/preload.ts +++ b/ts/util/preload.ts @@ -4,6 +4,7 @@ import { ipcRenderer } from 'electron'; import { strictAssert } from './assert'; +import * as Errors from '../types/errors'; import type { UnwrapPromise } from '../types/Util'; import type { IPCEventsValuesType, @@ -104,11 +105,7 @@ export function installSetting( try { ipcRenderer.send('settings:response', seq, null, await getFn()); } catch (error) { - ipcRenderer.send( - 'settings:response', - seq, - error && error.stack ? error.stack : error - ); + ipcRenderer.send('settings:response', seq, Errors.toLogFormat(error)); } }); } @@ -132,11 +129,7 @@ export function installSetting( await setFn(value); ipcRenderer.send('settings:response', seq, null); } catch (error) { - ipcRenderer.send( - 'settings:response', - seq, - error && error.stack ? error.stack : error - ); + ipcRenderer.send('settings:response', seq, Errors.toLogFormat(error)); } }); } @@ -152,11 +145,7 @@ export function installCallback( try { ipcRenderer.send('settings:response', seq, null, await hook(...args)); } catch (error) { - ipcRenderer.send( - 'settings:response', - seq, - error && error.stack ? error.stack : error - ); + ipcRenderer.send('settings:response', seq, Errors.toLogFormat(error)); } }); } diff --git a/ts/util/processAttachment.ts b/ts/util/processAttachment.ts index 3d28766f9..13739303c 100644 --- a/ts/util/processAttachment.ts +++ b/ts/util/processAttachment.ts @@ -8,6 +8,7 @@ import type { AttachmentDraftType, InMemoryAttachmentDraftType, } from '../types/Attachment'; +import * as Errors from '../types/errors'; import { getMaximumAttachmentSize } from './attachments'; import { AttachmentToastType } from '../types/AttachmentToastType'; import { fileToBytes } from './fileToBytes'; @@ -105,7 +106,7 @@ export async function processAttachment( } catch (e) { log.error( `Was unable to generate thumbnail for fileType ${fileType}`, - e && e.stack ? e.stack : e + Errors.toLogFormat(e) ); const data = await fileToBytes(file); attachment = { @@ -125,7 +126,7 @@ export async function processAttachment( } catch (error) { log.error( 'Error ensuring that image is properly sized:', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); throw error; diff --git a/ts/util/queueAttachmentDownloads.ts b/ts/util/queueAttachmentDownloads.ts index fb02e2373..22f242692 100644 --- a/ts/util/queueAttachmentDownloads.ts +++ b/ts/util/queueAttachmentDownloads.ts @@ -19,6 +19,7 @@ import type { MessageAttributesType, QuotedMessageType, } from '../model-types.d'; +import * as Errors from '../types/errors'; import type { StickerType } from '../types/Stickers'; import type { LinkPreviewType } from '../types/message/LinkPreviews'; @@ -222,7 +223,7 @@ export async function queueAttachmentDownloads( } catch (error) { log.error( `Problem copying sticker (${packId}, ${stickerId}) to attachments:`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } diff --git a/ts/util/scaleImageToLevel.ts b/ts/util/scaleImageToLevel.ts index d885c9008..f183f7e94 100644 --- a/ts/util/scaleImageToLevel.ts +++ b/ts/util/scaleImageToLevel.ts @@ -8,7 +8,6 @@ import { IMAGE_JPEG } from '../types/MIME'; import { canvasToBlob } from './canvasToBlob'; import { getValue } from '../RemoteConfig'; import { parseNumber } from './libphonenumberUtil'; -import { isRecord } from './isRecord'; enum MediaQualityLevels { One = 1, @@ -126,13 +125,10 @@ export async function scaleImageToLevel( throw new Error('image not a canvas'); } ({ image } = data); - } catch (err) { - const errorString = isRecord(err) && 'stack' in err ? err.stack : err; - const error = new Error( - 'scaleImageToLevel: Failed to process image', - errorString - ); - error.originalError = err; + } catch (cause) { + const error = new Error('scaleImageToLevel: Failed to process image', { + cause, + }); throw error; } diff --git a/ts/util/sendToGroup.ts b/ts/util/sendToGroup.ts index da2685f3d..2b38c1978 100644 --- a/ts/util/sendToGroup.ts +++ b/ts/util/sendToGroup.ts @@ -5,6 +5,7 @@ import { differenceWith, omit, partition } from 'lodash'; import { ErrorCode, + LibSignalErrorBase, groupEncrypt, ProtocolAddress, sealedSenderMultiRecipientEncrypt, @@ -21,6 +22,7 @@ import { import { Address } from '../types/Address'; import { QualifiedAddress } from '../types/QualifiedAddress'; import { UUID } from '../types/UUID'; +import * as Errors from '../types/errors'; import { getValue, isEnabled } from '../RemoteConfig'; import type { UUIDStringType } from '../types/UUID'; import { isRecord } from './isRecord'; @@ -216,7 +218,7 @@ export async function sendContentMessageToGroup({ log.error( `sendToGroup/${logId}: Sender Key send failed, logging, proceeding to normal send`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } } @@ -581,7 +583,10 @@ export async function sendToGroupViaSenderKey(options: { recursionCount: recursionCount + 1, }); } - if (error.code === ErrorCode.InvalidRegistrationId && error.addr) { + if ( + error instanceof LibSignalErrorBase && + error.code === ErrorCode.InvalidRegistrationId + ) { const address = error.addr as ProtocolAddress; const name = address.name(); @@ -742,7 +747,7 @@ function getSenderKeyExpireDuration(): number { } catch (error) { log.warn( `getSenderKeyExpireDuration: Failed to parse integer. Using default of ${MAX_SENDER_KEY_EXPIRE_DURATION}.`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); return MAX_SENDER_KEY_EXPIRE_DURATION; } @@ -753,7 +758,10 @@ export function _shouldFailSend(error: unknown, logId: string): boolean { log.error(`_shouldFailSend/${logId}: ${message}`); }; - if (error instanceof Error && error.message.includes('untrusted identity')) { + if ( + error instanceof LibSignalErrorBase && + error.code === ErrorCode.UntrustedIdentity + ) { logError("'untrusted identity' error, failing."); return true; } @@ -1271,7 +1279,7 @@ async function fetchKeysForIdentifiers( } catch (error) { log.error( 'fetchKeysForIdentifiers: Failed to fetch keys:', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); throw error; } diff --git a/ts/views/conversation_view.tsx b/ts/views/conversation_view.tsx index 728c4188c..6135d7812 100644 --- a/ts/views/conversation_view.tsx +++ b/ts/views/conversation_view.tsx @@ -12,6 +12,7 @@ import { render } from 'mustache'; import type { AttachmentType } from '../types/Attachment'; import { isGIF } from '../types/Attachment'; import * as Stickers from '../types/Stickers'; +import * as Errors from '../types/errors'; import type { DraftBodyRangesType } from '../types/Util'; import type { MIMEType } from '../types/MIME'; import type { ConversationModel } from '../models/conversations'; @@ -1693,7 +1694,7 @@ export class ConversationView extends window.Backbone.View { } catch (error) { log.error( 'Error sending delete-for-everyone', - error && error.stack, + Errors.toLogFormat(error), messageId ); showToast(ToastDeleteForEveryoneFailed); @@ -2375,7 +2376,7 @@ export class ConversationView extends window.Backbone.View { const { packId, stickerId } = options; this.model.sendStickerMessage(packId, stickerId); } catch (error) { - log.error('clickSend error:', error && error.stack ? error.stack : error); + log.error('clickSend error:', Errors.toLogFormat(error)); } } @@ -2531,10 +2532,7 @@ export class ConversationView extends window.Backbone.View { } } catch (error) { this.enableMessageField(); - log.error( - 'sendMessage error:', - error && error.stack ? error.stack : error - ); + log.error('sendMessage error:', Errors.toLogFormat(error)); return; } @@ -2596,7 +2594,7 @@ export class ConversationView extends window.Backbone.View { } catch (error) { log.error( 'Error pulling attached files before send', - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); } finally { this.enableMessageField(); diff --git a/ts/window.d.ts b/ts/window.d.ts index 5b58c3ea6..a0e27d684 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -3,13 +3,10 @@ // Captures the globals put in place by preload.js, background.js and others -/* eslint-disable max-classes-per-file */ - import type { Store } from 'redux'; import type * as Backbone from 'backbone'; import type * as Underscore from 'underscore'; import type PQueue from 'p-queue/dist'; -import type { Ref } from 'react'; import type { assert } from 'chai'; import type * as Mustache from 'mustache'; @@ -358,15 +355,6 @@ declare global { }; } - interface Error { - originalError?: Event; - reason?: unknown; - stackForLog?: string; - - // Used in sticker creator to attach messages to errors - errorMessageI18nKey?: string; - } - interface Element { // WebKit-specific scrollIntoViewIfNeeded: (bringToCenter?: boolean) => void; @@ -387,19 +375,6 @@ declare global { } } -export class GumVideoCapturer { - constructor( - maxWidth: number, - maxHeight: number, - maxFramerate: number, - localPreview: Ref - ); -} - -export class CanvasVideoRenderer { - constructor(canvas: Ref); -} - export type WhisperType = { Conversation: typeof ConversationModel; ConversationCollection: typeof ConversationModelCollectionType; diff --git a/ts/windows/main/phase1-ipc.ts b/ts/windows/main/phase1-ipc.ts index aeadb3105..e2fe2b71a 100644 --- a/ts/windows/main/phase1-ipc.ts +++ b/ts/windows/main/phase1-ipc.ts @@ -11,6 +11,7 @@ import { ThemeType } from '../../types/Util'; import { getEnvironment, Environment } from '../../environment'; import { SignalContext } from '../context'; import * as log from '../../logging/log'; +import * as Errors from '../../types/errors'; import { strictAssert } from '../../util/assert'; @@ -90,7 +91,7 @@ window.isBeforeVersion = (toCheck, baseVersion) => { } catch (error) { log.error( `isBeforeVersion error: toCheck: ${toCheck}, baseVersion: ${baseVersion}`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); return true; } @@ -101,7 +102,7 @@ window.isAfterVersion = (toCheck, baseVersion) => { } catch (error) { log.error( `isBeforeVersion error: toCheck: ${toCheck}, baseVersion: ${baseVersion}`, - error && error.stack ? error.stack : error + Errors.toLogFormat(error) ); return true; } @@ -305,7 +306,7 @@ ipc.on('delete-all-data', async () => { try { await deleteAllData(); } catch (error) { - log.error('delete-all-data: error', error && error.stack); + log.error('delete-all-data: error', Errors.toLogFormat(error)); } }); @@ -362,10 +363,7 @@ ipc.on('get-ready-for-shutdown', async () => { await shutdown(); ipc.send('now-ready-for-shutdown'); } catch (error) { - ipc.send( - 'now-ready-for-shutdown', - error && error.stack ? error.stack : error - ); + ipc.send('now-ready-for-shutdown', Errors.toLogFormat(error)); } }); diff --git a/ts/workers/heicConverterWorker.ts b/ts/workers/heicConverterWorker.ts index caed5af0b..88707e2a5 100644 --- a/ts/workers/heicConverterWorker.ts +++ b/ts/workers/heicConverterWorker.ts @@ -18,7 +18,7 @@ const port = parentPort; function respond(uuid: string, error: Error | undefined, response?: File) { const wrappedResponse: WrappedWorkerResponse = { uuid, - error: error ? error.stack : undefined, + error: error?.stack, response, }; port.postMessage(wrappedResponse);