Why am I getting "H12 Request timeout" errors in NodeJS?


The Heroku Dashboard Metrics and my application logs are showing H12 "Request timeout" errors but there is no debugging information, even when using New Relic, Trace or OpBeat.


This article refers to an example application from here.

What happens

Node doesn't implicitly handle requests by sending responses - that responsibility is up to you as the developer. A common mistake in node applications is to provide responses in some branches, but leave out responses in other logical branches.

In this server, routes with 'works' in their url are handled, but routes that fail the if test are left to linger.

Noticing that the app hasn't sent a response within 30 seconds, Heroku's router triggers an H12.

How to fix it

If your app triggers H12s, the first thing you should do is add logs along every branch of the failing route (every if statement, function call, etc). It's best if these logs include the x-request-id header so you can easily grep for a single request thread.

Then, make a request to that route and see where the request ends in the logs. What is the last successful step?

Finally, investigate the code to find which branch doesn't provide a response.

With Express, following the chain-of-responsibility pattern can help minimize this sort of logical error. Instead of this:

app.get('/app/:id', doEverythingInOneHugeFunctionWithAsyncBranches);

Use this:

app.get('/app/:id', checkUserAuth, findApp, renderView, sendJSON);

function checkUserAuth(req, res, next) {
  if (req.session.user) return next();
  return next(new NotAuthorizedError());

You'll also get the added benefit of unified error handling through the error middleware stack.