routing-controllers icon indicating copy to clipboard operation
routing-controllers copied to clipboard

'Can't set headers after they are sent' error when using Express middleware 'after' request.

Open nathanosdev opened this issue 8 years ago • 9 comments

import {Middleware, ExpressErrorMiddlewareInterface} from "routing-controllers";

@Middleware({ type: "after" })
export class CustomErrorHandler implements ExpressErrorMiddlewareInterface {

    error(error: any, request: any, response: any, next: (err: any) => any) {
        console.log("do something...");
        next();
    }

}

This example code from the documentation causes a 'Can't set headers after they are sent' error. Changing the type to 'before' fixes the issue.

Error: Can't set headers after they are sent.
    at validateHeader (_http_outgoing.js:504:11)
    at ServerResponse.setHeader (_http_outgoing.js:511:3)
    at dnsPrefetchControl (C:\Programming\pbc\server\auth-server\node_modules\dns-prefetch-control\index.js:9:11)
    at call (C:\Programming\pbc\server\auth-server\node_modules\connect\index.js:239:7)
    at next (C:\Programming\pbc\server\auth-server\node_modules\connect\index.js:183:5)
    at Function.handle (C:\Programming\pbc\server\auth-server\node_modules\connect\index.js:186:3)
    at app (C:\Programming\pbc\server\auth-server\node_modules\connect\index.js:51:37)
    at Layer.handle [as handle_request] (C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\layer.js:95:5)
    at trim_prefix (C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\index.js:317:13)
    at C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\index.js:284:7
    at Function.process_params (C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\index.js:335:12)
    at next (C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\index.js:275:10)
    at compression (C:\Programming\pbc\server\auth-server\node_modules\compression\index.js:220:5)
    at Layer.handle [as handle_request] (C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\layer.js:95:5)
    at trim_prefix (C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\index.js:317:13)
    at C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\index.js:284:7

nathanosdev avatar Aug 29 '17 17:08 nathanosdev

But then setting type to 'before', the error handler won't catch the error! 😆

Disabling the default error handler and setting the type back to 'after' seems to be working combination so there's a reasonable workaround. But the way documentation is written, it looks like the original code I posted above should work without error even with the default error handler.

nathanosdev avatar Aug 29 '17 17:08 nathanosdev

You simply needs to left out the next(); call and the error will go away.

We will rework this part, see progress in #256 & #144 and a few other.

NoNameProvided avatar Aug 30 '17 07:08 NoNameProvided

Is that the case for just 'after' typed middleware? It makes sense as to why this issue happens now that I've played around with it and I've got working middleware with no issues. I don't think there's any bug here, but the documentation needs to be updated with a working example that won't throw an error 😀

nathanosdev avatar Aug 30 '17 09:08 nathanosdev

I get this Can't set headers after they are sent even with no `next() method

@Middleware({ type: "after" })
export class CustomErrorHandler implements ExpressErrorMiddlewareInterface {

    error(error: any, request: any, response: any, next: (err: any) => any) {
        console.log("error...");
        response.status(error.status || 500)
            .json({
                name   : error.name,
                message: error.message,
                status : error.httpCode,
                stack  : error.stack,
            })
    }

}

What is sending the headers before response.status.json?

IAMtheIAM avatar Sep 11 '17 17:09 IAMtheIAM

Default error handler.

MichalLytek avatar Sep 11 '17 18:09 MichalLytek

For people coming here from the internet, from my research it seems like something like class-validators will set the headers for you, hence why you're getting this error.

I was able to work around it by checking for res.headersSent. Here's my full middleware:

import * as express from 'express';
import { ExpressErrorMiddlewareInterface, HttpError, Middleware } from 'routing-controllers';

import { Logger, ILoggerInterface } from '../../decorators/logger';
import { env } from '../../env';

@Middleware({ type: 'after' })
export class ErrorHandlerMiddleware implements ExpressErrorMiddlewareInterface {

  isProduction = env.isProduction;

  constructor(
    @Logger(__filename) private log: ILoggerInterface
  ) { }

  error(error: HttpError, req: express.Request, res: express.Response, next: express.NextFunction): void {
    // It seems like some decorators handle setting the response (i.e. class-validators)
    if (!res.headersSent) {
      res.status(error.httpCode || 500);

      res.json({
        name: error.name,
        message: error.message,
        errors: error['errors'] || [],
      });
    }

    if (this.isProduction) {
      this.log.error(error.name, error.message);
    } else {
      this.log.error(error.name, error.stack);
    }
  }
}

et avatar Mar 16 '18 20:03 et

Reopening to clarify this in the docs.

NoNameProvided avatar Mar 18 '18 22:03 NoNameProvided

I realize this might be trivial for others, but for me (and, possibly, future Internet searchers), even after reading through this thread and the full intro page for the project, it took a while (and some code-digging) to realize that if you want to use your own error handler middleware, you're probably better off disabling the default one:

createExpressServer({
  defaultErrorHandler: false,
  // ...etc.

That way you can set your error headers and response bodies however you want, without having to worry about the headers being set before your middleware had a chance to do it.

madve2 avatar Nov 14 '18 14:11 madve2

Stale issue message

github-actions[bot] avatar Feb 14 '20 00:02 github-actions[bot]