Improve error handling during group sends

This commit is contained in:
Fedor Indutny 2022-11-22 10:43:43 -08:00 committed by GitHub
parent f0a3735ca2
commit 991580a1ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 299 additions and 324 deletions

View file

@ -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);

View file

@ -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)
);
}
}
});

View file

@ -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);

View file

@ -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,
});
}
});

View file

@ -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);
}
}

View file

@ -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'
);

View file

@ -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;
}

View file

@ -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)
);
}

View file

@ -609,7 +609,7 @@ export async function startApp(): Promise<void> {
} 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<void> {
} 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<void> {
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<void> {
} 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<void> {
} 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<void> {
} 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<void> {
} 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<void> {
} 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<void> {
} 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<void> {
} 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<void> {
} 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<void> {
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<void> {
log.error(
'unlinkAndDisconnect: Something went wrong clearing ' +
'local configuration',
eraseError && eraseError.stack ? eraseError.stack : eraseError
Errors.toLogFormat(eraseError)
);
} finally {
window.Signal.Util.Registration.markEverDone();

View file

@ -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);
}

View file

@ -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 {

View file

@ -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)
);
}

View file

@ -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<Date>): 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;
}
}

View file

@ -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}`);
});

View file

@ -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<DeleteModel> {
this.remove(del);
});
} catch (error) {
log.error(
'Deletes.onDelete error:',
error && error.stack ? error.stack : error
);
log.error('Deletes.onDelete error:', Errors.toLogFormat(error));
}
}
}

View file

@ -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<MessageReceiptModel> {
this.remove(receipt);
} catch (error) {
log.error(
'MessageReceipts.onReceipt error:',
error && error.stack ? error.stack : error
);
log.error('MessageReceipts.onReceipt error:', Errors.toLogFormat(error));
}
}
}

View file

@ -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<MessageRequestModel> {
this.remove(sync);
} catch (error) {
log.error(
'MessageRequests.onResponse error:',
error && error.stack ? error.stack : error
);
log.error('MessageRequests.onResponse error:', Errors.toLogFormat(error));
}
}
}

View file

@ -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<ReactionModel> {
this.remove(reaction);
});
} catch (error) {
log.error(
'Reactions.onReaction error:',
error && error.stack ? error.stack : error
);
log.error('Reactions.onReaction error:', Errors.toLogFormat(error));
}
}
}

View file

@ -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));
}
}
}

View file

@ -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<ViewOnceOpenSyncModel> {
this.remove(sync);
} catch (error) {
log.error(
'ViewOnceOpenSyncs.onSync error:',
error && error.stack ? error.stack : error
);
log.error('ViewOnceOpenSyncs.onSync error:', Errors.toLogFormat(error));
}
}
}

View file

@ -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));
}
}
}

View file

@ -1242,7 +1242,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} 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<MessageAttributesType> {
}
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<MessageAttributesType> {
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<MessageAttributesType> {
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;
}

View file

@ -144,7 +144,7 @@ export async function routineProfileRefresh({
} catch (err) {
log.error(
`${logId}: refreshed profile for ${conversation.idForLogging()}`,
err?.stack || err
Errors.toLogFormat(err)
);
}
}

View file

@ -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}`
);

View file

@ -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 {

View file

@ -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,

View file

@ -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'

View file

@ -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;
}

View file

@ -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)
);
}

View file

@ -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<void> {
try {
@ -28,7 +29,7 @@ export async function deleteAllData(): Promise<void> {
} catch (error) {
log.error(
'Something went wrong deleting all data:',
error && error.stack ? error.stack : error
Errors.toLogFormat(error)
);
}
window.restart();

View file

@ -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);

View file

@ -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' });
}
};

View file

@ -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<void> {
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<void> {
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));
});
}
}

View file

@ -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}

View file

@ -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'));
});

View file

@ -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 {

View file

@ -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;
}
}

View file

@ -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(', ');
}
}

View file

@ -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 {

View file

@ -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;
}
})
);
}

View file

@ -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';

View file

@ -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 {}

View file

@ -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));
}
}

View file

@ -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

View file

@ -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',

View file

@ -102,7 +102,7 @@ export async function onRetryRequest(event: RetryRequestEvent): Promise<void> {
} 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;

View file

@ -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;
}

View file

@ -13,9 +13,9 @@ const loadImageData = async (input: Input): Promise<ImageData> => {
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;
}

View file

@ -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<T>({
@ -62,7 +63,7 @@ export async function longRunningTaskWrapper<T>({
} catch (error) {
log.error(
`longRunningTaskWrapper/${idLog}: Error!`,
error && error.stack ? error.stack : error
Errors.toLogFormat(error)
);
clearTimeoutIfNecessary(progressTimeout);

View file

@ -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<Name extends keyof IPCEventsCallbacksType>(
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));
}
});
}

View file

@ -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;

View file

@ -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)
);
}
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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<ConversationModel> {
} 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<ConversationModel> {
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<ConversationModel> {
}
} 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<ConversationModel> {
} catch (error) {
log.error(
'Error pulling attached files before send',
error && error.stack ? error.stack : error
Errors.toLogFormat(error)
);
} finally {
this.enableMessageField();

25
ts/window.d.ts vendored
View file

@ -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<HTMLVideoElement>
);
}
export class CanvasVideoRenderer {
constructor(canvas: Ref<HTMLCanvasElement>);
}
export type WhisperType = {
Conversation: typeof ConversationModel;
ConversationCollection: typeof ConversationModelCollectionType;

View file

@ -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));
}
});

View file

@ -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);