Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeError: handler is not a function before server is fully online #49536

Closed
1 task done
masterkain opened this issue May 9, 2023 · 6 comments · Fixed by #49548
Closed
1 task done

TypeError: handler is not a function before server is fully online #49536

masterkain opened this issue May 9, 2023 · 6 comments · Fixed by #49548
Labels
area: app App directory (appDir: true) bug Issue was opened via the bug report template.

Comments

@masterkain
Copy link

masterkain commented May 9, 2023

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
      Platform: linux
      Arch: x64
      Version: #1 SMP Tue Sep 13 07:51:46 UTC 2022
    Binaries:
      Node: 20.1.0
      npm: 9.6.4
      Yarn: 1.22.19
      pnpm: N/A
    Relevant packages:
      next: 13.4.1
      eslint-config-next: 13.4.1
      react: 18.2.0
      react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

To Reproduce

I'm building the app for docker in standalone mode, so I start the server with node server.js.
I'm also deploying in kubernetes, and this is happening. at the moment it's unclear if it's happening because traffic is hitting the pod before the server is fully ready or there's something else.

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  output: 'standalone'
};

module.exports = nextConfig;

Describe the Bug

│ TypeError: handler is not a function                                                                                                                                               │
│     at Server.<anonymous> (/app/server.js:29:11)                                                                                                                                   │
│     at Server.emit (node:events:511:28)                                                                                                                                            │
│     at parserOnIncoming (node:_http_server:1109:12)                                                                                                                                │
│     at HTTPParser.parserOnHeadersComplete (node:_http_common:119:17)                                                                                                               │
│ TypeError: handler is not a function                                                                                                                                               │
│     at Server.<anonymous> (/app/server.js:29:11)                                                                                                                                   │
│     at Server.emit (node:events:511:28)                                                                                                                                            │
│     at parserOnIncoming (node:_http_server:1109:12)                                                                                                                                │
│     at HTTPParser.parserOnHeadersComplete (node:_http_common:119:17)                                                                                                               │
│ TypeError: handler is not a function                                                                                                                                               │
│     at Server.<anonymous> (/app/server.js:29:11)                                                                                                                                   │
│     at Server.emit (node:events:511:28)                                                                                                                                            │
│     at parserOnIncoming (node:_http_server:1109:12)                                                                                                                                │
│     at HTTPParser.parserOnHeadersComplete (node:_http_common:119:17)                                                                                                               │
│ Listening on port 3000 url: http://airbroke-web-f88f4dbc7-dvxqb:3000     

Expected Behavior

I'm debugging an issue with prisma connections and stumbled upon this, so I took a look at the compiled server.js

maybe to ensure the server is fully initialized before it starts accepting incoming traffic, we can move the handler initialization to be before the createServer call, like this?

(async () => {
  handler = await createServerHandler({
    port: currentPort,
    hostname,
    dir,
    conf: nextConfig,
  })

  const server = http.createServer(async (req, res) => {
    try {
      await handler(req, res)
    } catch (err) {
      console.error(err);
      res.statusCode = 500
      res.end('Internal Server Error')
    }
  })

  if (
    !Number.isNaN(keepAliveTimeout) &&
    Number.isFinite(keepAliveTimeout) &&
    keepAliveTimeout >= 0
  ) {
    server.keepAliveTimeout = keepAliveTimeout
  }
  server.listen(currentPort, (err) => {
    if (err) {
      console.error("Failed to start server", err)
      process.exit(1)
    }

    console.log(
      'Listening on port',
      currentPort,
      'url: http://' + hostname + ':' + currentPort
    )
  })
})()

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

@masterkain masterkain added the bug Issue was opened via the bug report template. label May 9, 2023
@github-actions github-actions bot added the area: app App directory (appDir: true) label May 9, 2023
@ferdingler
Copy link
Contributor

ferdingler commented May 9, 2023

The issue appears to have been introduced here: 83b774e where the creation of the Next Server now returns a promise that takes a while to resolve

@masterkain
Copy link
Author

that's interesting, that also might be source of my prisma issues

@ferdingler
Copy link
Contributor

ferdingler commented May 9, 2023

Here is a server.js that works if it helps you:

const http = require("http");
const path = require("path");
const {
  createServerHandler,
} = require("next/dist/server/lib/render-server-standalone");

const dir = path.join(__dirname);

process.env.NODE_ENV = "production";
process.chdir(__dirname);

// Make sure commands gracefully respect termination signals (e.g. from Docker)
// Allow the graceful termination to be manually configurable
if (!process.env.NEXT_MANUAL_SIG_HANDLE) {
  process.on("SIGTERM", () => process.exit(0));
  process.on("SIGINT", () => process.exit(0));
}

const currentPort = parseInt(process.env.PORT, 10) || 3000;
const hostname = process.env.HOSTNAME || "localhost";
const keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10);

// const nextConfig = {}; // ommitted for brevity

process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig);

createServerHandler({
  port: currentPort,
  hostname,
  dir,
  conf: nextConfig,
}).then((nextHandler) => {
  console.log("Creating http server");
  const server = http.createServer(async (req, res) => {
    try {
      console.log("Handling request");
      await nextHandler(req, res);
    } catch (err) {
      console.error("Failed to call handler", err);
      res.statusCode = 500;
      res.end("Internal Server Error");
    }
  });

  if (
    !Number.isNaN(keepAliveTimeout) &&
    Number.isFinite(keepAliveTimeout) &&
    keepAliveTimeout >= 0
  ) {
    console.log("Setting keep alive timeout", keepAliveTimeout);
    server.keepAliveTimeout = keepAliveTimeout;
  }

  server.listen(currentPort, async (err) => {
    if (err) {
      console.error("Failed to start server", err);
      process.exit(1);
    }

    console.log(
      "Listening on port",
      currentPort,
      "url: http://" + hostname + ":" + currentPort
    );
  });
}).catch(err => {
  console.error("Failed to create next server handler", err);
  process.exit(1);
});

You can copy it manually to the standalone directory to override the one Next.js generates after running a build:

cp server.js .next/standalone/server.js

@petertflem
Copy link

petertflem commented May 10, 2023

I might have seen the same issue. We're on 13.4. What I saw was a blank page, with only the word "Internal server error" written. After a few moments, the error went away. Has happened twice now. Checked the logs right after the last time, and I have this error with a timestamp that looked reasonable:

TypeError: handler is not a function

We are using output: 'standalone' also, the site running in a docker container. Tried to reproduce it locally with just building it and running with node .next/standalone/server.js, but it didn't work. Maybe it would have to be in a docker container, or some other processes to slow things down.

So I am not a 100% sure it is the same issue, but seems plausible.

EDIT: Managed to trigger it by changing revisions in GCP. Here is a screenshot of what I see:
image

ijjk added a commit that referenced this issue May 11, 2023
…t handler is ready (#49548)

### Fixing a bug

Fixes #49536
Fixes #49338

### What?
Starting on next.js 13.4.0, the `server.js` file created by the
standalone output is incorrect because it does not wait until the Next
handler is ready before node http server starts accepting requests.

Initial requests will produce the following error:

```
TypeError: handler is not a function
at Server.<anonymous> (/tmp/app/server.js:29:11)
at Server.emit (node:events:513:28)
at parserOnIncoming (node:_http_server:998:12)
at HTTPParser.parserOnHeadersComplete (node:_http_common:128:17)
```

### Why?
The creation of the next server takes roughly 800ms on a basic hello
world app which causes the initial requests to fail because the
`handler` variable is not initialized.

### Solution
This changes the order of code so that Next Server is initialized before
the Node HTTP server and awaits until the promise is completed to kick
off the HTTP server.

### Alternative code

```js
let handler;

const server = http.createServer(async (req, res) => {
  try {
    await waitUntilHandlerIsReady(); // <---- wait here
    await handler(req, res);
  } catch (err) {
    console.error("Failed to call handler", err);
    res.statusCode = 500;
    res.end("Internal Server Error");
  }
});

async function waitUntilHandlerIsReady() {
  if (!handler) {
    await new Promise((resolve) => setTimeout(resolve, 200));
    await waitUntilHandlerIsReady();
  }
}
```

---------

Co-authored-by: Tim Neutkens <tim@timneutkens.nl>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
@julieca-magloft
Copy link

I might have seen the same issue. We're on 13.4. What I saw was a blank page, with only the word "Internal server error" written. After a few moments, the error went away. Has happened twice now. Checked the logs right after the last time, and I have this error with a timestamp that looked reasonable:

TypeError: handler is not a function

We are using output: 'standalone' also, the site running in a docker container. Tried to reproduce it locally with just building it and running with node .next/standalone/server.js, but it didn't work. Maybe it would have to be in a docker container, or some other processes to slow things down.

So I am not a 100% sure it is the same issue, but seems plausible.

EDIT: Managed to trigger it by changing revisions in GCP. Here is a screenshot of what I see: image

I experience the same as you, do you already have any clue?

@github-actions
Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 14, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: app App directory (appDir: true) bug Issue was opened via the bug report template.
Projects
None yet
4 participants