@@ -87,6 +87,18 @@ export interface ErrorInformation {
8787 readonly documentationUri ?: vscode . Uri
8888}
8989
90+ export class UnknownError extends Error {
91+ public override readonly name = 'UnknownError'
92+
93+ public constructor ( public readonly cause : unknown ) {
94+ super ( String ( cause ) )
95+ }
96+
97+ public static cast ( obj : unknown ) : Error {
98+ return obj instanceof Error ? obj : new UnknownError ( obj )
99+ }
100+ }
101+
90102/**
91103 * Anonymous class with a pre-defined error name.
92104 */
@@ -217,9 +229,44 @@ export class ToolkitError extends Error implements ErrorInformation {
217229 }
218230}
219231
232+ export function getErrorMsg ( err : Error | undefined ) : string | undefined {
233+ if ( err === undefined ) {
234+ return undefined
235+ }
236+
237+ // error_description is a non-standard SDK field added by (at least) OIDC service.
238+ // If present, it has better information, so prefer it to `message`.
239+ // https://github.com/aws/aws-toolkit-jetbrains/commit/cc9ed87fa9391dd39ac05cbf99b4437112fa3d10
240+ //
241+ // Example:
242+ //
243+ // [error] API response (oidc.us-east-1.amazonaws.com /token): {
244+ // name: 'InvalidGrantException',
245+ // '$fault': 'client',
246+ // '$metadata': {
247+ // httpStatusCode: 400,
248+ // requestId: '7f5af448-5af7-45f2-8e47-5808deaea4ab',
249+ // extendedRequestId: undefined,
250+ // cfId: undefined
251+ // },
252+ // error: 'invalid_grant',
253+ // error_description: 'Invalid refresh token provided',
254+ // message: 'UnknownError'
255+ // }
256+ const anyDesc = ( err as any ) . error_description
257+ const errDesc = typeof anyDesc === 'string' ? anyDesc . trim ( ) : ''
258+ const msg = errDesc !== '' ? errDesc : err . message ?. trim ( )
259+
260+ if ( typeof msg !== 'string' ) {
261+ return undefined
262+ }
263+
264+ return msg
265+ }
266+
220267export function formatError ( err : Error ) : string {
221268 const code = hasCode ( err ) && err . code !== err . name ? `[${ err . code } ]` : undefined
222- const parts = [ `${ err . name } :` , err . message , code , formatDetails ( err ) ]
269+ const parts = [ `${ err . name } :` , getErrorMsg ( err ) , code , formatDetails ( err ) ]
223270
224271 return parts . filter ( isNonNullable ) . join ( ' ' )
225272}
@@ -249,18 +296,6 @@ function formatDetails(err: Error): string | undefined {
249296 return `(${ joined } )`
250297}
251298
252- export class UnknownError extends Error {
253- public override readonly name = 'UnknownError'
254-
255- public constructor ( public readonly cause : unknown ) {
256- super ( String ( cause ) )
257- }
258-
259- public static cast ( obj : unknown ) : Error {
260- return obj instanceof Error ? obj : new UnknownError ( obj )
261- }
262- }
263-
264299export function getTelemetryResult ( error : unknown | undefined ) : Result {
265300 if ( error === undefined ) {
266301 return 'Succeeded'
@@ -273,38 +308,10 @@ export function getTelemetryResult(error: unknown | undefined): Result {
273308
274309/** Gets the (partial) error message detail for the `reasonDesc` field. */
275310export function getTelemetryReasonDesc ( err : unknown | undefined ) : string | undefined {
276- if ( err === undefined ) {
277- return undefined
278- }
311+ const msg = getErrorMsg ( err as Error )
279312
280- const e = err as any
281- // error_description is a non-standard SDK field added by OIDC service.
282- // It has improved messages, so prefer it if found.
283- // https://github.com/aws/aws-toolkit-jetbrains/commit/cc9ed87fa9391dd39ac05cbf99b4437112fa3d10
284- //
285- // Example:
286- //
287- // [error] API response (oidc.us-east-1.amazonaws.com /token): {
288- // name: 'InvalidGrantException',
289- // '$fault': 'client',
290- // '$metadata': {
291- // httpStatusCode: 400,
292- // requestId: '7f5af448-5af7-45f2-8e47-5808deaea4ab',
293- // extendedRequestId: undefined,
294- // cfId: undefined
295- // },
296- // error: 'invalid_grant',
297- // error_description: 'Invalid refresh token provided',
298- // message: 'UnknownError'
299- // }
300- const msg = e ?. error_description ?. trim ( ) ? e ?. error_description ?. trim ( ) : e ?. message ?. trim ( )
301-
302- if ( typeof msg === 'string' ) {
303- // Truncate to 200 chars.
304- return msg !== '' ? msg . substring ( 0 , 200 ) : undefined
305- }
306-
307- return undefined
313+ // Truncate to 200 chars.
314+ return msg && msg . length > 0 ? msg . substring ( 0 , 200 ) : undefined
308315}
309316
310317export function getTelemetryReason ( error : unknown | undefined ) : string | undefined {
@@ -336,7 +343,8 @@ export function getTelemetryReason(error: unknown | undefined): string | undefin
336343export function resolveErrorMessageToDisplay ( error : unknown , defaultMessage : string ) : string {
337344 const mainMessage = error instanceof ToolkitError ? error . message : defaultMessage
338345 // We want to explicitly show certain AWS Error messages if they are raised
339- const prioritizedMessage = findPrioritizedAwsError ( error ) ?. message
346+ const awsError = findPrioritizedAwsError ( error )
347+ const prioritizedMessage = getErrorMsg ( awsError )
340348 return prioritizedMessage ? `${ mainMessage } : ${ prioritizedMessage } ` : mainMessage
341349}
342350
@@ -352,19 +360,10 @@ export const prioritizedAwsErrors: RegExp[] = [
352360]
353361
354362/**
355- * Sometimes there are AWS specific errors that we want to explicitly
356- * show to the user, these are 'prioritized' errors.
363+ * Tries to find the most useful/relevant error to surface to the user.
357364 *
358- * In certain cases we may unknowingly wrap these errors in a Toolkit error
359- * as the 'cause', in return masking the the underlying error from being
365+ * Errors may be wrapped anywhere in the codepath, which may prevent the root cause from being
360366 * reported to the user.
361- *
362- * Since we do not want developers to worry if they are allowed to wrap
363- * a specific AWS error in a Toolkit error, we will instead handle
364- * it in this function by extracting the 'prioritized' error if it is
365- * found.
366- *
367- * @returns new ToolkitError with prioritized error message, otherwise original error
368367 */
369368export function findPrioritizedAwsError (
370369 error : unknown ,
@@ -380,30 +379,25 @@ export function findPrioritizedAwsError(
380379}
381380
382381/**
383- * This will search through the causal chain of errors (if it exists)
384- * until it finds an {@link AWSError}.
382+ * Searches through the chain of errors (if any) until it finds an {@link AWSError}.
385383 *
386384 * {@link ToolkitError} instances can wrap a 'cause', which is the underlying
387385 * error that caused it.
388386 *
389- * @returns AWSError if found, otherwise undefined
387+ * @returns AWSError, or undefined
390388 */
391389export function findAwsErrorInCausalChain ( error : unknown ) : AWSError | undefined {
392390 let currentError = error
393391
394- while ( currentError !== undefined ) {
392+ for ( let i = 0 ; currentError && i < 100 ; i ++ ) {
395393 if ( isAwsError ( currentError ) ) {
396394 return currentError
397395 }
398396
399- // TODO: Base Error has 'cause' in ES2022. If we upgrade this can be made
400- // non-ToolkitError specific
401- if ( currentError instanceof ToolkitError && currentError . cause !== undefined ) {
402- currentError = currentError . cause
403- continue
397+ // Note: Base Error has 'cause' in ES2022. So does our own `ToolkitError`.
398+ if ( ( currentError as any ) . cause !== undefined ) {
399+ currentError = ( currentError as any ) . cause
404400 }
405-
406- return undefined
407401 }
408402
409403 return undefined
0 commit comments