Deploying Node.js Apps on Google App Engine: Typical Server Errors & Solutions

How to Debug Google App Engine Errors

In recent years, I’ve witnessed the transformative impact of serverless computing on the evolution of DevOps. By embracing serverless technologies, you can unlock scalability and simplify your deployment processes while creating more bang for your buck. The modular architecture and reduced infrastructure management allows our teams to innovate rapidly, delivering exceptional value to our clients with speed and efficiency.

In this article, I will showcase some of the most confusing errors that commonly happen during Node.js deployments on Google’s Serverless App Engine and solutions to them. Node.js is the commonly utilized JavaScript runtime environment and JavaScript (Js) is the most commonly utilized programming language for web apps (WordPress effect needs to be removed from statistics as it’s a widely-adapted mostly no-code CMS solution which utilizes PHP). Throughout the years, JS remains many developers’ first web app development programming language choice.

Let’s check out the errors.

How to Debug Google App Engine Errors

First of all let’s briefly discuss how you can actually identify errors in this specific case which is when deploying apps to Google App Engine. While some errors will be clearly visible in your command line or directly in the browser (such as 500 Internal Server Error), other errors will be hard to identify without the right tool.
 
App Engine Logs are a great example to error identification tools (and info about many other things) which you can access through multiple links/windows.
  • If you click the App Engine’s Navigation Menu (very top left, hamburger button), you will then see
    • Logging Menu > Logs Explorer.
  • Alternatively, you can click Navigation Menu again and then App Engine > Services
    • Your default service will have a last column called Diagnose under which you can see the link to Logs.

You might need to adjust date and time of the Logs Explorer or regenerate the errors by attempting to run the app or its functions but Logs Explorer is a very powerful debugging tool which will show you detailed information about what is wrong and what type of errors your app is facing.

Let’s start checking out the actual errors that might be occurring.

Alternative Debugging Method

Alternatively, you can always go to your browser’s developer tools and see the logs and errors on the front end of the application but we can assume that many server-side errors won’t actually show up here hence App Engine’s Logs Explorer is a lot more relevant in this case.

Google App Engine Errors & Solutions Provided Below

The errors I’ve dealt with can be listed as:

  • TypeError
  • Cannot Get / Error
  • syntaxerror missing ) after argument list
  • Google App Engine 503 Service Unavailable Error
  • ERROR: (gcloud.app.deploy)
  • ERROR: (gcloud.app.deploy) INVALID_ARGUMENT
  • Infinite Page Loading due to port error
  • 500 Internal Server Error

Below you can find solutions to all these typical App Engine Errors.

Server.js or Index.js related TypeError

This error typically means server.js file is not declaring the app properly hence the issues with app.use function as the app doesn’t have use method.

Make sure you have imported the necessary modules correctly. In many cases, this error occurs when the express module is not imported or initialized properly. Ensure that you have the following line at the beginning of your server.js file:

Verify that you have created an instance of the Express application using express() or const app = express(). This creates an Express application object that has the use method and other middleware functions. Make sure you have the following code before using app.use:

const express = require(‘express’);

const app = express();

Check if you have any typos or syntax errors in your code. The error could be caused by a mistake in the syntax or incorrect usage of the Express is a middleware here and any typo or syntax error will also cause similar app.use TypeError on the server.

Additionally, you must ensure dependencies are defined properly so  that the necessary Express modules are not missing or not properly installed.

By reviewing and addressing these points, you should be able to resolve the TypeError: app.use is not a function issue in your server.js file.

An example from Google Cloud Service Health Page

TypeError: app.use is not a function
at Object.<anonymous> (/workspace/server.js:6:5)
at Module._compile (node:internal/modules/cjs/loader:1255:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1309:10)
at Module.load (node:internal/modules/cjs/loader:1113:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12)
at node:internal/main/run_main_module:23:47

For example, Node.js app below defines app variable as require(‘express’), but instead of creating an app, this code line attempts to import the express library. As a result, since app isn’t created properly and program won’t be able to call use method on it, we will get a basic example of TypeError.

const app = require('express');

app.get('/', (req, res) => {
  res.send('Hello from App Engine!');
});

app.use(express.static(__dirname + '/public'));

// Listen to the App Engine-specified port, or 8080 otherwise
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}...`);
});

In this case, it should be pretty easy to fix the error by properly creating the app variable. Here is the fixed code:

We are importing Express and creating an Express App the right way in this example.

const express = require('express');
const app = express();

app.use(express.static(__dirname + '/public'));

app.get('/', (req, res) => {
  res.send('Hello from App Engine!');
});

// Listen to the App Engine-specified port, or 8080 otherwise
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}...`);
});

In addition to TypeError, Node.js has below error classes that can be useful in debugging the applications/deployments.

  • Class: AssertionError
  • Class: RangeError
  • Class: ReferenceError
  • Class: SyntaxError
  • Class: SystemError

Cannot GET / error

This error signifies that your app is not able to get the public folder. Most likely culprit is wrong definition of static folders or issues with public folder and its front-end content (typically index.html).

Simply fix by adding static folder entry to the server-side code (typically index.js or server.js) as below:

app.use(express.static('public'));

Here is a common directory structure of a very basic Node.js app:

- project_directory
  - public
    - index.html
  - index.js
  - package.json
- app.yaml

And here is a common directory structure of a very extensive Node.js app:

- project_directory
  - src
    - controllers
    - models
    - routes
    - services
    - utils
  - public
    - css
    - js
    - images
  - views
  - config
  - tests
  - node_modules
  - index.js
  - package.json
- app.yaml

503 System Unavailable error

This is likely the least likely error to happen. 503 indicates something is wrong with the availability of the Google Cloud or Google App Engine Services. Luckily Google Cloud is a very robust IT service and 503 errors will occur very seldom and for very short periods of time.

Other than waiting or reaching out to Google Cloud Customer Support, you can monitor Google Cloud’s System Health page where they list the status of Google Services across many regions.

An example from Google Cloud Service Health Page

syntaxerror missing ) after argument list error

Another particular error is a syntaxerror pointing to a missing parenthesis. In reality, there can be so many reasons for this error.

First of all, obviously make sure your index.js, server.js etc. files aren’t missing a parenthesis. In my experience it can be a missing curly bracket as well and sometimes App Engine will throw the same syntax errorr.

Additionally, it’s not uncommon to have a version conflict to cause this error as different versions of nodejs can interpret the files differently. Make sure to define your runtime in app.yaml:

nodejs20

Also make sure to define your engine version if needed:

nodejs version =  20.3.0

An example from Google Cloud Service Health Page
import express, { Request, Response } from "express";

const app = express();
const port = 3000;

app.get("/", (req: Request, res: Response) => {
  res.send("Hello, World!");
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
};

For example the JS code sample above uses express but there is a rookie syntax error. Last line is supposed to be

});

rather than just };

These little mistakes happen all the time.

(gcloud.app.deploy) error

If you forget to include a crucial statement or make syntax errors in your configuration files such as app.yaml or packages.json, you will get a somewhat generic error as ERROR: gcloud.app.deploy. You can see the actual error statement below.

ERROR: (gcloud.app.deploy) An error occurred while parsing file: […/app.yaml]

In this case, you have to make sure mandatory statements are included and there aren’t any syntax errors in your files. An example to this is stating the runtime of the application and it can be simply done by including this line in your app.yaml:

runtime: nodejs20

If you make syntax mistakes similar to below or put something App Engine doesn’t work with, you will still get a deploy error as app.yaml can’t be parsed by the App Engine platform.

runtime nodejs20 (Colon is missing)

:runtime: nodejs20 (Colon syntax error)

(gcloud.app.deploy) INVALID_ARGUMENT error

In app.yaml file, one thing you will be required to include is the runtime variable. As mentioned above typically it will be something like this for JavaScript applications: runtime: nodejs19
An example from Google Cloud Service Health Page

The issue here is that nodejs19 is not an available runtime environment in App Engine.

Available Node.js runtimes on Google App Engine are:

  • nodejs10
  • nodejs12
  • nodejs14
  • nodejs16
  • nodejs18
  • nodejs20

If you specify something like nodejs19, you will get the error below.

Simply assign an available runtime and the error will be resolved.

ERROR: (gcloud.app.deploy) INVALID_ARGUMENT: Invalid runtime ‘nodejs19’ specified. Accepted runtimes are: [php, php55, python27, java, java7, java8, nodejs18, php72, nodejs16, php74, python310, php73, python311, nodejs10, ruby30, nodejs14, nodejs12, go120, ruby32, java17, php82, php81, java11, go118, go119, go116, go114, go115, nodejs20, go112, python38, go113, python37, go111, python39, ruby27, ruby26, ruby25]

Here is a correct app.yaml example:

runtime: nodejs20

(gcloud.app.deploy) error due to missing app.yaml

This one is quite self-explanatory. App Engine needs an app.yaml file in the application’s root folder even if it’s just a one-line runtime assignment. If this file is completely missing, you will get a deployment error.

ERROR: An app.yaml (or appengine-web.xml) file is required to deploy this directory as an App Engine application. Create an app.yaml file using the directions at https://cloud.google.com/appengine/docs/flexible/reference/app-yaml (App Engine flexible environment) or https://cloud.google.com/appengine/docs/standard/reference/app-yaml (App Engine standard environment) under the tab for your language.

ERROR: (gcloud.app.deploy) [/home/GAE-gcloud-tests] could not be identified as a valid source directory or file.

Solution is simple, check that you have an app.yaml file with the right file name and extension. Also check that it’s where it’s supposed to be, which is the root folder of your app.

Infinite Page Loading due to port error

Typically, App Engine serves applications on port 8080. Normally, you don’t have to specify any other port and you can just use the following port values in your server-side app (such as server.js or index.js files). A mismatch between application’s port assignment and Google App Engine’s port availability will cause an error due to request deadline being exceeded without any activity.

process.env.PORT || 8080

After the timeout you can go to the Log section of your Google Cloud Account either via the main Logging section from the main service selection usually on the left side or under App Engine’s Service section you can find a “See Logs” shortcut near your related service. In the logs you will see an error like below.

Error: Process terminated because the request deadline was exceeded. Please ensure that your HTTP server is listening for requests on 0.0.0.0 and on the port defined by the PORT environment variable. (Error code 123)

Assigning a different port number will cause an app to load infinitely since Google App Engine is serving the application on a different port than the application is attempting to listen. Google App Engine’s port is fixed and cannot be changed by the individual application.

So, if your browser wheel is turning for an unexpectedly long time and the request eventually times out without anything happening, the most likely culprit is a wrong port value being used in the app’s server-side JavaScript code.

This can be a very tricky error especially for the inexperienced DevOps engineers since there won’t be any specific errors during or before the deployment nor during the execution of the app. App will deploy fine and work fine locally (perhaps on port:3000 which is commonly used in development of Node.js apps), browser won’t give any errors, browser’s developer tools and debuggers won’t show any errors and eventually timeout will occur.

const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}...`);
});

Above, as a common practice port is assigned to 3000 for local development purposes. Although nothing’s wrong with this code, it is the wrong setup for App Engine deployment which uses port 8080 by default.

Simply solve infinite loading application error and timeout by assigning the right port environment variable according to App Engine as below:

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});

Google App Engine 500 Internal Server Error

500 Internal Server Error is an umbrella error code for all server errors. You might have heard the saying in IT that there is nothing the user can do about the 500 Internal Server Error. But if you think about it, when you are deploying a Node.js app on Google App Engine, your app is the server. In fact, Node.js itself is the most commonly used JavaScript Runtime Environment.that takes care of server-side of the application.

So if there is any incompatibility, conflict, error, glitch, bug in anywhere in your code or your app’s back-end or front-end or any configuration, you might see this annoying error. You might think, how am I supposed to find what the exact issue is? In this article, I will point out various methods to pinpoint the most common errors and provide solutions to these common errors.

Here are 2 configuration-related bugs that will cause App Engine deployment to throw 500 Internal Server Error:
(In most cases, fixing either one will eliminate 500 Internal Server Error but following both practices won’t cause any damage.)

Error: Server Error
The server encountered an error and could not complete your request.

Please try again in 30 seconds.

Entrypoint causing 500 Internal Server Error

A pretty common cause of 500 Internal Server Error with Google App Engine is having a different entrypoint name than default and not specifying it appropriately.

For example with nodejs runtime, App Engine looks for server.js file as default entrypoint. Entrypoint is the main server-side program that initiates serving the application and its front-end. So many developers will choose a file name such as index.js for their application’s main server-side code. This is doable as long as entrypoint is specified in the app.yaml file that instructs Google App Engine. It’s a quite simple fix. Here is the code example:

runtime: nodejs20
entrypoint: node ./index.js

Start Script (package.json) causing 500 Internal Server Error

This will solve the Internal 500 Error as App Engine doesn’t try to initiate an nonexistent server.js file. It’s equally important to assign a start script to the same file via package.json file. package.json file acts as a configuration file for your Node.js project. It contains metadata about your project, including its name, version, dependencies, and various scripts that can be executed using npm. When you assign the start script, and App Engine runs npm start by default to initiate JavaScript apps in Node.js runtime environment, npm start automatically knows the start script.

But when you don’t include the start script in package.json, the application can only be run by node index.js command which isn’t something App Engine does by default. This is the reason why neglecting start script in package.json will cause 500 Internal Server Error with App Engine. You can easily address this by adding following start line to your app’s package.json file under scripts dictionary.

  "scripts": {
    "start": "node index.js",
  }

500 Internal Server Error due to wrong file references

Another example to why 500 Internal Server Error will occur in your app is when you are referencing other style and functional files such as css or javascript but they don’t exist or file path or file names are wrong.

If you look at the simple index.html below, we are calling two external files, a css file and a js file. Since styles.css is misnamed as stylez.css and stylez.css doesn’t exist server will look for the wrong file and throw an Internal Server Error. Solution is to fix the file name but also it’d be very smart to deploy a bare bones application without any file references first to be able to isolate the errors.

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" type="text/css" href="css/stylez.css">
  <script src="js/script.js"></script>
</head>
<body>
  <h1 id="Hello World"></h1>
</body>
</html>

If you fix the styles.css file name above, or completely remove the line for test purposes, the error will disappear (or you’ll be able to catch other errors.)

500 Internal Server Error due to await command inside a non-async function

If you use the await keyword inside a non-async function, it will result in a syntax error. The await keyword can only be used inside an asynchronous function marked with the async keyword.

The purpose of the await keyword is to pause the execution of an asynchronous function until a Promise is resolved. It allows you to write asynchronous code that looks and behaves more like synchronous code.

When you use await inside an asynchronous function, it waits for the Promise to settle (either resolve or reject) and then continues the execution of the function. The await expression itself returns the resolved value of the Promise.

If you attempt to use await outside of an async function, the JavaScript engine will raise a syntax error because it doesn’t expect the await keyword in that context. To use await, you need to wrap it inside an asynchronous function.

const express = require('express');
const app = express();
const port = 3000;

function fetchData() {
  return new Promise((resolve) => {
    setTimeout(() => resolve('Data has been fetched!'), 2000);
  });
}

app.get('/', (req, res) => {
  res.send('Hello World!');

  try {
    const result = await fetchData(); // Syntax Error: await is only valid in async function
    console.log(result);
  } catch (error) {
    console.error(error);
  }
});

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`);
});

Summary

In this Node.jsApp Engine debugging tutorial we covered a number of errors that can be quite puzzling. The article doesn’t cover all of the html or server-related errors extensively but it aims to provide guidance and solutions to some of the most confusing and least explanatory errors you can get after deploying on App Engine. Good examples to such confusing errors are generic 500 Internal Server Error and infinite page loading until timeout (or just white screen with nothing loading).