diff --git a/core/packages/google-auth-library-nodejs/src/auth/oauth2common.ts b/core/packages/google-auth-library-nodejs/src/auth/oauth2common.ts index 3b1de4419e11..e8e02e2cc124 100644 --- a/core/packages/google-auth-library-nodejs/src/auth/oauth2common.ts +++ b/core/packages/google-auth-library-nodejs/src/auth/oauth2common.ts @@ -255,8 +255,11 @@ export function getErrorFromOAuthErrorResponse( if (key !== 'message') { Object.defineProperty(newError, key, { value: (err as {} as {[index: string]: string})[key], - writable: false, - enumerable: true, + // Keep stack writable, configurable and non-enumerable, as on a + // regular Error, so callers can still append context to it. + writable: key === 'stack', + enumerable: key !== 'stack', + configurable: key === 'stack', }); } }); diff --git a/core/packages/google-auth-library-nodejs/test/test.oauth2common.ts b/core/packages/google-auth-library-nodejs/test/test.oauth2common.ts index c722b9c91646..c4d3226928f8 100644 --- a/core/packages/google-auth-library-nodejs/test/test.oauth2common.ts +++ b/core/packages/google-auth-library-nodejs/test/test.oauth2common.ts @@ -500,4 +500,24 @@ describe('getErrorFromOAuthErrorResponse', () => { assert.strictEqual(actualError.name, expectedError.name); assert.strictEqual(actualError.stack, expectedError.stack); }); + + it('should keep the copied stack writable, configurable and non-enumerable', () => { + const originalError = new Error('Original error message'); + const resp = { + error: 'invalid_grant', + error_description: 'ID Token is stale to sign-in.', + }; + + const actualError = getErrorFromOAuthErrorResponse(resp, originalError); + const descriptor = Object.getOwnPropertyDescriptor(actualError, 'stack'); + assert.strictEqual(descriptor?.writable, true); + assert.strictEqual(descriptor?.enumerable, false); + assert.strictEqual(descriptor?.configurable, true); + // Consumers append causal context to error.stack; in strict mode that + // throws a TypeError when the property is read-only. + assert.doesNotThrow(() => { + actualError.stack += '\nCaused by: wrapped error'; + }); + assert.ok(actualError.stack?.endsWith('\nCaused by: wrapped error')); + }); });