Repository: novuhq/novu
File: apps/api/src/exception-filter.ts (TypeScript)

Code before:
    }
    if (this.isBadRequestWithMultipleExceptions(exception)) {
      return this.handleValidationPipeValidation(exception, request);
    }

    if (exception instanceof HttpException && !(exception instanceof InternalServerErrorException)) {
      return this.handleOtherHttpExceptions(exception, request);
    }

    if (this.isPayloadTooLargeError(exception)) {
      return this.handleOtherHttpExceptions(new PayloadTooLargeException(), request);
    }

    return this.buildA5xxError(request, exception);
  }

  private isPayloadTooLargeError(exception: unknown) {
    return exception?.constructor?.name === 'PayloadTooLargeError';
  }

  private isBadRequestWithMultipleExceptions(exception: unknown): exception is ValidationPipeError {
    // noinspection UnnecessaryLocalVariableJS
    const isBadRequestExceptionFromValidationPipe =
      exception instanceof Object &&
      'response' in exception &&
      'message' in (exception as any).response &&
      Array.isArray((exception as any).response.message);

    return isBadRequestExceptionFromValidationPipe;
  }
  private buildA5xxError(request: Request, exception: unknown) {
    const errorDto500 = this.buildErrorDto(request, HttpStatus.INTERNAL_SERVER_ERROR, ERROR_MSG_500);

    return {
      ...errorDto500,
      errorId: this.getUuid(exception),
    };
  }

  private handleOtherHttpExceptions(exception: HttpException, request: Request): ErrorDto {
    const status = exception.getStatus();
    const response = exception.getResponse();
    const { innerMsg, tempContext } = this.buildMsgAndContextForHttpError(response, status);

    return this.buildErrorDto(request, status || 500, innerMsg, tempContext);
  }

  private buildMsgAndContextForHttpError(response: string | object | { message: string }, status: number) {
    if (typeof response === 'string') {
      return { innerMsg: response as string };
    }

Review comment (suggestion):
Most of our exceptions are not objects like `HttpException({...})` but rather `HttpException('string', code)` so using `in` operator on primitive values will result in runtime error

Code after:
    }
    if (this.isBadRequestWithMultipleExceptions(exception)) {
      return this.handleValidationPipeValidation(exception, request);
    }

    if (exception instanceof HttpException && !(exception instanceof InternalServerErrorException)) {
      return this.handleOtherHttpExceptions(exception, request);
    }

    if (this.isPayloadTooLargeError(exception)) {
      return this.handleOtherHttpExceptions(new PayloadTooLargeException(), request);
    }

    return this.buildA5xxError(request, exception);
  }

  private isPayloadTooLargeError(exception: unknown) {
    return exception?.constructor?.name === 'PayloadTooLargeError';
  }

  private isBadRequestWithMultipleExceptions(exception: unknown): exception is ValidationPipeError {
    // noinspection UnnecessaryLocalVariableJS
    const isBadRequestExceptionFromValidationPipe =
      exception instanceof Object &&
      safeHasProperty(exception, 'response') &&
      safeHasProperty((exception as any).response, 'message') &&
      Array.isArray((exception as any).response.message);

    return isBadRequestExceptionFromValidationPipe;
  }
  private buildA5xxError(request: Request, exception: unknown) {
    const errorDto500 = this.buildErrorDto(request, HttpStatus.INTERNAL_SERVER_ERROR, ERROR_MSG_500);

    return {
      ...errorDto500,
      errorId: this.getUuid(exception),
    };
  }

  private handleOtherHttpExceptions(exception: HttpException, request: Request): ErrorDto {
    const status = exception.getStatus();
    const response = exception.getResponse();
    const { innerMsg, tempContext } = this.buildMsgAndContextForHttpError(response, status);

    return this.buildErrorDto(request, status || 500, innerMsg, tempContext);
  }

  private buildMsgAndContextForHttpError(response: string | object | { message: string }, status: number) {
    if (typeof response === 'string') {
      return { innerMsg: response as string };
    }