nest icon indicating copy to clipboard operation
nest copied to clipboard

Exception handler with Fastify leaks internals if content type is set

Open thomaschaaf opened this issue 2 years ago • 5 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Current behavior

import { Controller, Get, Header } from '@nestjs/common';

@Controller()
export class AppController {
  @Get()
  @Header('Content-Type', 'text/xml')
  get(): string {
    throw new Error();
    // return 'ok';
  }
}

Throw an error:

[1635460397235] ERROR (6802 on MacBook-Pro.fritz.box): Promise errored, but reply.sent = true was set
    reqId: "req-1"
    err: {
      "type": "FastifyError",
      "message": "Attempted to send payload of invalid type 'object'. Expected a string or Buffer.",
      "stack":
          FastifyError: Attempted to send payload of invalid type 'object'. Expected a string or Buffer.
              at onSendEnd (/Users/thomaschaaf/develop/x/x-api/node_modules/fastify/lib/reply.js:430:11)
              at onSendHook (/Users/thomaschaaf/develop/x/x-api/node_modules/fastify/lib/reply.js:390:5)
              at _Reply.Reply.send (/Users/thomaschaaf/develop/x/x-api/node_modules/fastify/lib/reply.js:193:3)
              at FastifyAdapter.reply (/Users/thomaschaaf/develop/x/x-api/node_modules/@nestjs/platform-fastify/adapters/fastify-adapter.js:46:29)
              at ExceptionsHandler.handleUnknownError (/Users/thomaschaaf/develop/x/x-api/node_modules/@nestjs/core/exceptions/base-exception-filter.js:38:24)
              at ExceptionsHandler.catch (/Users/thomaschaaf/develop/x/x-api/node_modules/@nestjs/core/exceptions/base-exception-filter.js:17:25)
              at ExceptionsHandler.next (/Users/thomaschaaf/develop/x/x-api/node_modules/@nestjs/core/exceptions/exceptions-handler.js:16:20)
              at Object.<anonymous> (/Users/thomaschaaf/develop/x/x-api/node_modules/@nestjs/core/router/router-proxy.js:13:35)
              at processTicksAndRejections (internal/process/task_queues.js:93:5)
      "name": "FastifyError",
      "code": "FST_ERR_REP_INVALID_PAYLOAD_TYPE",
      "statusCode": 500
    }

and results in a response like this:

{
  "statusCode": 500,
  "code": "FST_ERR_REP_INVALID_PAYLOAD_TYPE",
  "error": "Internal Server Error",
  "message": "Attempted to send payload of invalid type 'object'. Expected a string or Buffer."
}

Minimum reproduction code

See above

Steps to reproduce

No response

Expected behavior

Should not pass fastify error to the enduser

Package

  • [ ] I don't know. Or some 3rd-party package
  • [ ] @nestjs/common
  • [ ] @nestjs/core
  • [ ] @nestjs/microservices
  • [ ] @nestjs/platform-express
  • [X] @nestjs/platform-fastify
  • [ ] @nestjs/platform-socket.io
  • [ ] @nestjs/platform-ws
  • [ ] @nestjs/testing
  • [ ] @nestjs/websockets
  • [ ] Other (see below)

Other package

No response

NestJS version

8.1.2

Packages versions

Node.js version

No response

In which operating systems have you tested?

  • [X] macOS
  • [ ] Windows
  • [ ] Linux

Other

No response

thomaschaaf avatar Oct 28 '21 22:10 thomaschaaf

I faced the same issue. I was trying to set application/pdf value in the Content-Type but got the same error code FST_ERR_REP_INVALID_PAYLOAD_TYPE

  @Get('/export')
  @Header('Content-Type', 'application/pdf')
  export(): Promise<Buffer> {
    return this.service.generatePDF();
  }

Ensaphelon avatar Nov 02 '21 07:11 Ensaphelon

Here's a repo reproducing this: https://gitlab.com/micalevisk/nestjs-issue-8469


For some reason, when FastifyAdapter is used, the following line is evaluated twice

https://github.com/nestjs/nest/blob/411ef5e237b487c447e6b28fafaf3914ffa75778/packages/platform-fastify/adapters/fastify-adapter.ts#L293

(after adding console.log({body}) before L293 above)

image

which means that the following function is running twice

https://github.com/nestjs/nest/blob/775a34853a2ab97c38e021c96c6a4ed2de7815fb/packages/core/exceptions/base-exception-filter.ts#L25

first with exception being the user-land error, and the other one being FastifyError.

While when using ExpressAdapter it's not (which is expected):

image

When @Header('Content-Type', 'application/pdf') is not used (fastify):

image

micalevisk avatar Dec 23 '21 03:12 micalevisk

Well, so this one is problematic. The default, built-in exception filter automatically converts all errors to a JSON-compatible format (to respond with the "Internal server error" in case it received an unexpected error it cannot recognize/classify).

Now, if you change the content type to, let's say, application/pdf (or XML), Fastify throws an exception because the exception filter automatically tries to respond with the JSON object. However, we can't really add checks for all possible content types (because that wasn't the goal from the early beginning) you could potentially use in your app to the exception filter. I'm not even sure if we should classify this as a "bug", instead, I think we should just add a warning (within the filter) that the default response format is JSON and if you want to auto-react to errors thrown from within handlers that support xml/etc you should attach a custom filter (to format the error accordingly).

kamilmysliwiec avatar Dec 23 '21 08:12 kamilmysliwiec

any idea how to solve this? I want to return Buffer but i cannot. I cannot believe that there is an issue to return other format in Fastify like pdf or so...

vytautas-pranskunas- avatar Mar 11 '22 16:03 vytautas-pranskunas-

@vytautas-pranskunas- I believe an workaround would be using your own expection filter that catches everything (like this one)

micalevisk avatar Mar 11 '22 18:03 micalevisk

Let's track this here https://github.com/nestjs/nest/pull/10474

kamilmysliwiec avatar Oct 31 '22 08:10 kamilmysliwiec