NestJS errors throw an internal sentry error
Is there an existing issue for this?
- [x] I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
- [x] I have reviewed the documentation https://docs.sentry.io/
- [x] I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases
How do you use Sentry?
Self-hosted/on-premise
Which SDK are you using?
@sentry/nestjs
SDK Version
10.29.0
Framework Version
@nestjs/core 11.1.9
Link to Sentry event
No response
Reproduction Example/SDK Setup
import 'dotenv/config';
import * as Sentry from '@sentry/nestjs';
const environment = process.env;
console.log('Instrumenting Sentry...');
Sentry.init({
// ...
enabled: environment.API_STAGE != 'dev',
spotlight: environment.SENTRY_SPOTLIGHT,
// ...
enableLogs: true,
});
// Then elsewhere, when the nest app (Which in this case is a Discord bot) starts up, and an error gets thrown, attached below.
Steps to Reproduce
- Init Sentry
- Wait for Sentry to start up
- In one of the files that has code for the Discord slash command, I run
throw new Error("Hello chat!!!");to test this feature and I get that error.
Expected Result
Expected result is the error gets logged in the console and it gets sent to Sentry and Spotlight.
Actual Result
Instead of that happening, the following thing gets logged, and nothing gets sent to Sentry nor Spotlight:
Additional Context
Some errors still get sent into Sentry, but this sometimes also happens..
Priority
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it.
@BYK Since you wanted to be pinged for this.
Hi, are you using microservices and did you set up an exception filter? https://docs.sentry.io/platforms/javascript/guides/nestjs/#using-error-filters-for-specific-exception-types
Hi, are you using microservices and did you set up an exception filter? https://docs.sentry.io/platforms/javascript/guides/nestjs/#using-error-filters-for-specific-exception-types
Hey there! Thanks for the swift reply. No, I am not using microservices, and no I did not set up an exception filter. Is this required?
Actually, scratch that last part.
We are using the @SentryExceptionCaptured() decorator in the AllExceptionsFilter.
Code snippet:
// Package imports...
@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
private static readonly _logger = new Logger('ExceptionsHandler');
@SentryExceptionCaptured()
catch(exception: unknown, host: ArgumentsHost) {
if (!(exception instanceof HttpException))
return super.catch(exception, host);
const hostType = host.getType() as any;
if (hostType === 'necord') {
if (!(exception instanceof IntrinsicException)) {
AllExceptionsFilter._logger.error(exception);
}
return;
}
super.catch(exception, host);
}
public isHttpError(err: any): err is { statusCode: number; message: string } {
return err?.statusCode && err?.message;
}
}
I am also using this in my AppModule:
// ...
providers: [
{
provide: APP_FILTER,
useClass: SentryGlobalFilter,
},
// ...
Hey! Using the @SentryGlobalFilter should be redundant if you have a catch-all filter (like your AllExceptionsFilter), so you can try if removing that changes anything. Your catch-all filter setup looks correct at first glance though.
Where are you throwing your exception from (e.g. controller or service and what kind of decorators you use there)?
Hey! Using the
@SentryGlobalFiltershould be redundant if you have a catch-all filter (like yourAllExceptionsFilter), so you can try if removing that changes anything. Your catch-all filter setup looks correct at first glance though.Where are you throwing your exception from (e.g. controller or service and what kind of decorators you use there)?
Hey there! Yeah, I tried disaling SentryGloalFilter in my AppModule already, however this did not fix the issue.
I am throwing my exception inside of another module (Not the .module.ts file itself, but files within that module) that handled Discord bot interactions. I'm throwing it inside of a file which has the code that gets executed when said command is run by a user on Discord.
See: https://docs.nestjs.com/recipes/necord
Thanks for providing more info, we'll have a look.
@Asynchronite Have you registered your AllExceptionsFilter in your providers and did you confirm that it is actually being called? Generally it would be great if you could add some more logging to the exception filter and then paste the outputs here (so we can actually see what's going on in your setup, e.g. what branch is actually called, what hostType the exception has). Trying to reproduce this the error locally atm but so far wasn't able to, so more information would help.
Hi @nicohrubec!
I'm one of the developers that work alongside @Asynchronite in this project. Just wanted to add additional context to the matter that we're using a framework called Necord which combines both the framework of NestJS with built-in support for Discord.JS.
In regards to answering the questions you have; yes, the AllExceptionsFilter does fire and attached below is the source code and how the variables were formatted/printed.
Source code with printed statements:
@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
private static readonly _logger = new Logger('ExceptionsHandler');
@SentryExceptionCaptured()
catch(exception: unknown, host: ArgumentsHost) {
console.log("AllExceptionsFilter catch triggered!")
console.log(">>> exception=")
console.log(exception)
console.log("-----")
console.log(">>> host=")
console.log(host)
console.log("hostType=", host.getType());
if (!(exception instanceof HttpException)) {
console.log("this is not a http exception")
return super.catch(exception, host);
}
const hostType = host.getType() as any;
if (hostType === 'necord') {
if (!(exception instanceof IntrinsicException)) {
AllExceptionsFilter._logger.error(exception);
}
return;
}
super.catch(exception, host);
}
public isHttpError(err: any): err is { statusCode: number; message: string } {
return err?.statusCode && err?.message;
}
}
Error/Logs:
api-1 | AllExceptionsFilter catch triggered!
api-1 | >>> exception=
api-1 | 757 | if (status >= 400 && status < 500) {
api-1 | 758 | if (status === 401 && requestData.auth) {
api-1 | 759 | manager.setToken(null);
api-1 | 760 | }
api-1 | 761 | const data = await parseResponse(res);
api-1 | 762 | throw new DiscordAPIError(data, "code" in data ? data.code : data.error, status, method, url, requestData);
api-1 | ^
api-1 | error: Unknown interaction
api-1 | requestBody: {
api-1 | files: undefined,
api-1 | json: [Object ...],
api-1 | },
api-1 | rawError: {
api-1 | message: "Unknown interaction",
api-1 | code: 10062,
api-1 | },
api-1 | code: 10062,
api-1 | status: 404,
api-1 | method: "POST",
api-1 | url: "https://discord.com/api/v10/interactions/xxx/.../callback?with_response=false",
api-1 |
api-1 | at handleErrors (/usr/src/app/node_modules/@discordjs/rest/dist/index.js:762:13)
api-1 | at async runRequest (/usr/src/app/node_modules/@discordjs/rest/dist/index.js:866:29)
api-1 |
api-1 | -----
api-1 | >>> host=
api-1 | ExecutionContextHost {
api-1 | args: [
api-1 | [
api-1 | [Object ...]
api-1 | ], SlashCommandDiscovery {
api-1 | meta: [Object ...],
api-1 | reflector: [Object ...],
api-1 | subcommands: Map {},
api-1 | discovery: [Object ...],
api-1 | contextCallback: [AsyncFunction],
api-1 | getDescription: [Function: getDescription],
api-1 | setSubcommand: [Function: setSubcommand],
api-1 | ensureSubcommand: [Function: ensureSubcommand],
api-1 | getSubcommand: [Function: getSubcommand],
api-1 | getSubcommands: [Function: getSubcommands],
api-1 | getRawOptions: [Function: getRawOptions],
api-1 | getOptions: [Function: getOptions],
api-1 | execute: [Function: execute],
api-1 | isSlashCommand: [Function: isSlashCommand],
api-1 | toJSON: [Function: toJSON],
api-1 | getName: [Function: getName],
api-1 | setGuilds: [Function: setGuilds],
api-1 | hasGuild: [Function: hasGuild],
api-1 | isGlobal: [Function: isGlobal],
api-1 | getGuilds: [Function: getGuilds],
api-1 | getClass: [Function: getClass],
api-1 | getHandler: [Function: getHandler],
api-1 | setDiscoveryMeta: [Function: setDiscoveryMeta],
api-1 | setContextCallback: [Function: setContextCallback],
api-1 | isContextMenu: [Function: isContextMenu],
api-1 | isMessageComponent: [Function: isMessageComponent],
api-1 | isListener: [Function: isListener],
api-1 | isTextCommand: [Function: isTextCommand],
api-1 | isModal: [Function: isModal],
api-1 | }
api-1 | ],
api-1 | constructorRef: null,
api-1 | handler: null,
api-1 | contextType: "necord",
api-1 | setType: [Function: setType],
api-1 | getType: [Function: getType],
api-1 | getClass: [Function: getClass],
api-1 | getHandler: [Function: getHandler],
api-1 | getArgs: [Function: getArgs],
api-1 | getArgByIndex: [Function: getArgByIndex],
api-1 | switchToRpc: [Function: switchToRpc],
api-1 | switchToHttp: [Function: switchToHttp],
api-1 | switchToWs: [Function: switchToWs],
api-1 | }
api-1 | hostType= necord
api-1 | this is not a http exception
api-1 | [Nest] 107 - 12/09/2025, 6:28:08 PM ERROR [BotService] TypeError: this.raw.getHeader is not a function. (In 'this.raw.getHeader(key)', 'this.raw.getHeader' is undefined)
api-1 | at <anonymous> (/usr/src/app/node_modules/fastify/lib/reply.js:219:49)
api-1 | at reply (/usr/src/app/node_modules/@nestjs/platform-fastify/adapters/fastify-adapter.js:247:26)
api-1 | at handleUnknownError (/usr/src/app/node_modules/@nestjs/core/exceptions/base-exception-filter.js:46:28)
api-1 | at catch (/usr/src/app/node_modules/@sentry/nestjs/build/cjs/decorators.js:81:30)
api-1 | at next (/usr/src/app/node_modules/@nestjs/core/exceptions/external-exceptions-handler.js:14:29)
api-1 | at processTicksAndRejections (native:7:39)
Hey! Thanks for providing the logs, that helps a lot.
I thought about this a bit more, I think the following is happening:
- Your host type is
necord. However, since the exception you throw is not a HTTP exception you call theBaseExceptionFilter. - The
BaseExceptionFilterexpects a HTTP execution context, but since you are in anecordcontext this leads to this weird behavior. We had similar issue in the past, where we were trying to handle RPC exceptions in a HTTP context, which also led to issues. - You have specific logic to handle exceptions in the
necordcontext, but that logic is never reached because the exception is not aHTTPException.
I am not exactly sure why the exception is not showing up in Sentry, but probably because it crashes before we get a chance to send the event off. What you probably want to do is to move your necord host type branch before the "not http exception" branch to avoid this situation. I am hoping this should work then. If it still doesn't work, you can of course always manually capture the exception with Sentry.captureException() to make it work.
I also backlogged a ticket to add support for the necord host type in our SentryGlobalFilter, which is not currently supported. However, atm we are not spending much time on developing new features for the Nest SDK, so might take a while until this is implemented.