function handleError()

in ui/src/callWithTelemetryAndErrorHandling.ts [114:201]


function handleError(context: types.IActionContext, callbackId: string, error: unknown): void {
    let rethrow: boolean = false;
    const errorContext: types.IErrorHandlerContext = Object.assign(context, { error, callbackId });
    try {
        for (const handler of Object.values(errorHandlers)) {
            try {
                handler(errorContext);
            } catch {
                // don't block other handlers
            }
        }

        const errorData: types.IParsedError = parseError(errorContext.error);
        const unMaskedMessage: string = errorData.message;
        errorData.message = maskUserInfo(errorData.message, context.valuesToMask);
        if (errorData.stepName) {
            context.telemetry.properties.lastStep = errorData.stepName;
        }

        if (errorData.isUserCancelledError) {
            context.telemetry.properties.result = 'Canceled';
            context.errorHandling.suppressDisplay = true;
            context.errorHandling.rethrow = false;
        } else {
            context.telemetry.properties.result = 'Failed';
            context.telemetry.properties.error = errorData.errorType;
            context.telemetry.properties.errorMessage = errorData.message;
            context.telemetry.properties.stack = errorData.stack ? limitLines(errorData.stack, maxStackLines) : undefined;
            if (context.telemetry.suppressIfSuccessful || context.telemetry.suppressAll) {
                context.telemetry.properties.suppressTelemetry = 'true';
            }
        }

        const issue: IReportableIssue = {
            callbackId: errorContext.callbackId,
            error: errorData,
            issueProperties: context.errorHandling.issueProperties,
            time: Date.now()
        };

        if (!context.errorHandling.suppressDisplay || context.errorHandling.forceIncludeInReportIssueCommand) {
            cacheIssueForCommand(issue);
        }

        if (!context.errorHandling.suppressDisplay) {
            // Always append the error to the output channel, but only 'show' the output channel for multiline errors
            ext.outputChannel.appendLog(localize('outputError', 'Error: {0}', unMaskedMessage));

            let message: string;
            if (unMaskedMessage.includes('\n')) {
                ext.outputChannel.show();
                message = localize('multilineError', 'An error has occured. Check output window for more details.');
            } else {
                message = unMaskedMessage;
            }

            const items: MessageItem[] = [];
            if (!context.errorHandling.suppressReportIssue) {
                items.push(DialogResponses.reportAnIssue);
            }

            if (context.errorHandling.buttons) {
                items.push(...context.errorHandling.buttons);
            }

            // don't wait
            void window.showErrorMessage(message, ...items).then(async (result: MessageItem | types.AzExtErrorButton | undefined) => {
                if (result === DialogResponses.reportAnIssue) {
                    await reportAnIssue(issue);
                } else if (result && 'callback' in result) {
                    await result.callback();
                }
            });
        }

        if (context.errorHandling.rethrow) {
            rethrow = true;
            throw errorContext.error;
        }
    } catch (internalError) {
        if (rethrow) {
            // Only time an error is expected is in the `rethrow` case
            throw internalError;
        } else {
            sendHandlerFailedEvent(errorContext, 'error');
        }
    }
}