-1

I have the following node.js server:

const main = () => {
  log(`Starting server`)

  const exit = signal => {
    server.close(() => process.exit())
    log('Server is closed.')
  }
 
  const server = http.createServer(handleRequest)
  server.listen(process.env.PORT, process.env.HOST, () => log(`Server is listening on ${process.env.HOST}:${process.env.PORT}`))

  process.on('SIGING', exit)
  process.on('SIGTERM', exit)
  process.on('uncaughtException', (err, origin) => {
    log(`Process caught unhandled exception: ${err} ${origin}`, 'ERROR')
  })
}

When I first run the server, I see:

Server is listening on 127.0.0.1:8080

(which is expected)

I kill the server with ctrl-c

Subsequent attempts to start the server output:

[ 2023-05-09T00:29:53.264Z | INFO ] Process caught unhandled exception: Error: listen EADDRINUSE: address already in use "8080" uncaughtException ERROR

I have tried the following:

  • lsof
  • netstat
  • fuser
  • npx kill-port 8080
  • brew install htop
  • restarting the computer

specifics:

$ sudo lsof -i :8080
$ sudo lsof -t -i :8080
$ sudo lsof -t -i tcp:8080
$ sudo lsof -i tcp:8080
$ sudo lsof -i :8080 -v
$ sudo lsof > ~/temp_lsof.txt
$ sudo lsof -iTCP:8080 -sTCP:LISTEN
$ sudo netstat -tuln | grep 8080
$ sudo netstat -lnp | grep 8080
$ sudo netstat -tanl | grep 8080
$ sudo netstat -anp tcp | grep 8080

Each and every one of these commands resulted in no output whatsoever.

╭ ~/Desktop/my_api (express-postrgesql)
╰ % npx kill-port 8080
Could not kill process on port 8080. No process running on port.

╭ ~/Desktop/my_api (express-postrgesql)
╰ % node server.js
[ 2023-05-09T01:13:24.247Z | INFO ] Starting api server
[ 2023-05-09T01:13:24.249Z | INFO ] Process caught unhandled exception: Error: listen EADDRINUSE: address already in use "8080" uncaughtException ERROR

I can't seem to find a process listening on port 8080 and yet... node insists something is. Any suggestions?

EDIT:

Switched the PORT in .env to 8081 to test (something looks screwy with the fact that the port is in ""...):

╭ ~/Desktop/my_api (express-postrgesql)
╰ % node server.js
[ 2023-05-09T01:17:50.148Z | INFO ] Starting api server
[ 2023-05-09T01:17:50.150Z | INFO ] Server is listening on "127.0.0.1":"8081"
^C

╭ ~/Desktop/my_api (express-postrgesql)
╰ % node server.js
[ 2023-05-09T01:17:59.339Z | INFO ] Starting api server
[ 2023-05-09T01:17:59.341Z | INFO ] Process caught unhandled exception: Error: listen EADDRINUSE: address already in use "8081" uncaughtException ERROR

╭ ~/Desktop/my_api (express-postrgesql)
╰ % sudo lsof -i :8081
Password:

╭ ~/Desktop/my_api (express-postrgesql)
╰ %
tadman
  • 208,517
  • 23
  • 234
  • 262
Aaron Parisi
  • 464
  • 7
  • 15
  • How exactly did you use `lsof` and what was the result? Did restarting the computer temporarily resolve the issue or not? – Phil May 09 '23 at 01:00
  • @Phil `lsof -i :8080`, `lsof -t -i tcp:8080` `lsof -i :3000 -v` (all with and without `sudo`) - restarting the computer did not resolve the issue – Aaron Parisi May 09 '23 at 01:04
  • 1
    Please [edit] your question with what you tried and **what the result was** – Phil May 09 '23 at 01:08
  • 2
    The clue is in your logging. You appear to have quote characters (ie `"`) embedded in your environment variables. How are they defined? – Phil May 09 '23 at 01:22
  • 1
    Your server says 8081, your kill says 8080. – tadman May 09 '23 at 01:24
  • @Phil good question - if I define them inline in the server file (`process.env.PORT = "8080"`) (note the quotes) - my logs show `server is listening on 127.0.0.1:8080` (note the _lack_ of quotes). If I define them in my `.env` file (`process.env.PORT = "8080"`) then read them in manually, I get `server is listening on "127.0.0.1":"8080"`... – Aaron Parisi May 09 '23 at 01:27
  • The format of `.env` does not require quotes or spaces but if you're using `dotenv`, it usually handles them ok. How are you loading the `.env` file? – Phil May 09 '23 at 01:28

1 Answers1

0

The answer was in how I was creating the environment variables, specifically process.env.PORT. Big thanks to @Phil for his helpful questions.

If I say process.env.PORT = "8080" inside my server file, everything works ok. I mistook this to mean that I had to wrap my env variables in quotes when inside .env:

PORT="8080"

(Note - none of my other env variables in .env were wrapped in quotes, but I wasn't paying attention to those when I added HOST and PORT to .env)

When I read the env variables from .env via:

const dotEnv = fs.readFileSync(path.join(__dirname, '.env'), 'utf8')
dotEnv.split('\n').forEach(line => {
  const [ key, val ] = line.split('=')
  process.env[key] = val
})

HOST was wrapped in extra quotes.

I don't even really know how the server started in the first place... tbd on what server.listen("127.0.0.1", "8080", ...) does


EDIT

RE "what happens if I pass a string to server.listen()?

  • firstly, it appears that I am always passing a string to server.listen().
  • in the case of specifying process.env.PORT = "8080" in the server file itself, I was, somewhat obviously, setting process.env.PORT to a string - the string 8080
  • in the case of specifying process.env.PORT="8080" in my .env file, when I read the variable from the .env file to process.env, I read it as a string as well - the string "8080"
  • I was under the impression that server.listen() performs parseInt() on the params expecting numbers - in the case of the string 8080, parseInt() works fine.
  • parseInt("8080") returns NaN

NOW: I am under the impression that if a non-parsable string is passed to server.listen(), something should... not work. But I still saw the logged statements from server.listen()'s callback:

  server.listen(process.env.PORT, process.env.HOST, e => {
    if (e) {
      log(`server.listen() returned error: `, e, process.env.PORT)
      return
    }
    log(`Server is listening on ${process.env.HOST}:${process.env.PORT}`)
  })
[ 2023-05-09T04:09:18.211Z | INFO ] Server is listening on 127.0.0.1:"8080"

Per node docs, The last parameter callback will be added as a listener for the 'listening' event. - so the server does seem to be "listening".

I will find out:

  1. to what is the server "listening" (currently I'm only logging the value I passed in, which may differ from the value it ended up as?
  2. whatever port "8080" is - why is it not opened up after ctrl-c kills the server?
  3. if in fact server.listen() performs parseInt() on the arguments, resulting on a NaN, why doesn't server.listen() return an error?

EDIT

when loading the erroneously double-double quoted PORT from .env (PORT="8080"), inspecting the server object in server.listen()'s callback shows the following:

server.address()  // '"8080"'
server.listening  // true

(so to question 3. above, I don't think server.listen() is being passed NaN after all)

HOWEVER, if I correct this in .env (process.env.PORT=8080), we get the following:

server.address()  // { address: '127.0.0.1', family: 'IPv4', port: 8080 }
server.listening  // true

address() has the shape described in Net server.address() docs.


Also note:

if .env contains PORT=8080, in my server file, typeof process.env.PORT is string (it just so happens to be a string that could be coerced into an integer)

If I say, explicitly, server.listen('fake', ...), server.listen() "works" and server.address() is 'fake'

Aaron Parisi
  • 464
  • 7
  • 15