Skip to content

Commit 9fd5ff7

Browse files
telemetry: include chained error message in reasonDesc (#5373)
* telemetry: include chained error message in reasonDesc Problem: When we use the function getTelemetryReasonDesc() we would lose information about the underlying error in the case the error was a ToolkitError with an underlying chained error. Only the message of the ToolkitError was used. So in telemetry we would be missing some information about the real cause of the error. Solution: getTelemetryReasonDesc() will return a message which consists of all messages in the chain. It recursively calls in to the next chained error of a ToolkitError and appends it to the final output. This will give us some context about the higher level call as well as the root of the issue. The final message could look something like: "Message A::Message B::Message C" Signed-off-by: Nikolas Komonen <[email protected]> * fix PR comments Signed-off-by: Nikolas Komonen <[email protected]> --------- Signed-off-by: Nikolas Komonen <[email protected]>
1 parent 14f14d2 commit 9fd5ff7

File tree

2 files changed

+42
-10
lines changed

2 files changed

+42
-10
lines changed

packages/core/src/shared/errors.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,23 @@ export class ToolkitError extends Error implements ErrorInformation {
243243
}
244244
}
245245

246-
export function getErrorMsg(err: Error | undefined): string | undefined {
246+
/**
247+
* Derives an error message from the given error object.
248+
* Depending on the Error, the property used to derive the message can vary.
249+
*
250+
* @param withCause Append the message(s) from the cause chain, recursively.
251+
* The message(s) are delimited by ' | '. Eg: msg1 | causeMsg1 | causeMsg2
252+
*/
253+
export function getErrorMsg(err: Error | undefined, withCause = false): string | undefined {
247254
if (err === undefined) {
248255
return undefined
249256
}
250257

258+
const cause = (err as any).cause
259+
if (withCause && cause) {
260+
return `${err.message}${cause ? ' | ' + getErrorMsg(cause, true) : ''}`
261+
}
262+
251263
// Non-standard SDK fields added by the OIDC service, to conform to the OAuth spec
252264
// (https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1) :
253265
// - error: code per the OAuth spec
@@ -404,7 +416,7 @@ export function scrubNames(s: string, username?: string) {
404416
* @param err Error object, or message text
405417
*/
406418
export function getTelemetryReasonDesc(err: unknown | undefined): string | undefined {
407-
const m = typeof err === 'string' ? err : getErrorMsg(err as Error) ?? ''
419+
const m = typeof err === 'string' ? err : getErrorMsg(err as Error, true) ?? ''
408420
const msg = scrubNames(m, _username)
409421

410422
// Truncate to 200 chars.

packages/core/src/test/shared/errors.test.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
formatError,
1111
getErrorMsg,
1212
getTelemetryReason,
13+
getTelemetryReasonDesc,
1314
getTelemetryResult,
1415
isNetworkError,
1516
resolveErrorMessageToDisplay,
@@ -451,14 +452,33 @@ describe('util', function () {
451452
'unauthorized message'
452453
)
453454
assert.deepStrictEqual(getErrorMsg(undefined), undefined)
454-
const err = new TestAwsError('ValidationException', 'aws validation msg 1', new Date())
455-
assert.deepStrictEqual(getErrorMsg(err), 'aws validation msg 1')
456-
;(err as any).error_description = ''
457-
assert.deepStrictEqual(getErrorMsg(err), 'aws validation msg 1')
458-
;(err as any).error_description = {}
459-
assert.deepStrictEqual(getErrorMsg(err), 'aws validation msg 1')
460-
;(err as any).error_description = 'aws error desc 1'
461-
assert.deepStrictEqual(getErrorMsg(err), 'aws error desc 1')
455+
let awsErr = new TestAwsError('ValidationException', 'aws validation msg 1', new Date())
456+
assert.deepStrictEqual(getErrorMsg(awsErr), 'aws validation msg 1')
457+
;(awsErr as any).error_description = ''
458+
assert.deepStrictEqual(getErrorMsg(awsErr), 'aws validation msg 1')
459+
;(awsErr as any).error_description = {}
460+
assert.deepStrictEqual(getErrorMsg(awsErr), 'aws validation msg 1')
461+
;(awsErr as any).error_description = 'aws error desc 1'
462+
assert.deepStrictEqual(getErrorMsg(awsErr), 'aws error desc 1')
463+
464+
// Arg withCause=true
465+
awsErr = new TestAwsError('ValidationException', 'aws validation msg 1', new Date())
466+
let toolkitError = new ToolkitError('ToolkitError Message')
467+
assert.deepStrictEqual(getErrorMsg(toolkitError, true), 'ToolkitError Message')
468+
469+
toolkitError = new ToolkitError('ToolkitError Message', { cause: awsErr })
470+
assert.deepStrictEqual(getErrorMsg(toolkitError, true), `ToolkitError Message | aws validation msg 1`)
471+
472+
const nestedNestedToolkitError = new ToolkitError('C')
473+
const nestedToolkitError = new ToolkitError('B', { cause: nestedNestedToolkitError })
474+
toolkitError = new ToolkitError('A', { cause: nestedToolkitError })
475+
assert.deepStrictEqual(getErrorMsg(toolkitError, true), `A | B | C`)
476+
})
477+
478+
it('getTelemetryReasonDesc()', () => {
479+
const err = new Error('Cause Message a/b/c/d.txt')
480+
const toolkitError = new ToolkitError('ToolkitError Message', { cause: err })
481+
assert.deepStrictEqual(getTelemetryReasonDesc(toolkitError), 'ToolkitError Message | Cause Message x/x/x/x.txt')
462482
})
463483

464484
it('isNetworkError()', function () {

0 commit comments

Comments
 (0)