diff --git a/packages/remix/src/utils/instrumentServer.ts b/packages/remix/src/utils/instrumentServer.ts index 0b0dd7c18790..a39b12f7e46e 100644 --- a/packages/remix/src/utils/instrumentServer.ts +++ b/packages/remix/src/utils/instrumentServer.ts @@ -69,8 +69,41 @@ interface DataFunction { (args: DataFunctionArgs): Promise | Response | Promise | AppData; } +// Taken from Remix Implementation +// https://github.com/remix-run/remix/blob/7688da5c75190a2e29496c78721456d6e12e3abe/packages/remix-server-runtime/responses.ts#L54-L62 +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function isResponse(value: any): value is Response { + return ( + value != null && + /* eslint-disable @typescript-eslint/no-unsafe-member-access */ + typeof value.status === 'number' && + typeof value.statusText === 'string' && + typeof value.headers === 'object' && + typeof value.body !== 'undefined' + /* eslint-enable @typescript-eslint/no-unsafe-member-access */ + ); +} + +// Taken from Remix Implementation +// https://github.com/remix-run/remix/blob/7688da5c75190a2e29496c78721456d6e12e3abe/packages/remix-server-runtime/data.ts#L131-L145 +function extractData(response: Response): Promise { + const contentType = response.headers.get('Content-Type'); + + if (contentType && /\bapplication\/json\b/.test(contentType)) { + return response.json(); + } + + return response.text(); +} + function captureRemixServerException(err: Error, name: string): void { - captureException(err, scope => { + // Skip capturing if the thrown error is an OK Response + // https://remix.run/docs/en/v1/api/conventions#throwing-responses-in-loaders + if (isResponse(err) && err.status < 400) { + return; + } + + captureException(isResponse(err) ? extractData(err) : err, scope => { scope.addEventProcessor(event => { addExceptionMechanism(event, { type: 'instrument',