Three days of troubleshooting over one conversion… Yes, it happens. Here’s how.
We develop a software suite for cognitive testing, helping researchers detect early signs of dementia. The suite includes:
- A Swift-based iPad app
- A Node.js web application
One key feature is sending emails via an SMTP server, which we handle using Nodemailer on our web app’s server.
After successful staging tests, we deployed the changes to production and asked the client to test the feature from the user’s side. A while later, we got a report: the email never arrived.
Checking the logs, we saw a cryptic SSL error:
/Error: 40DCDF5E897F0000:error:0A00010B:SSL
routines:ssl3_get_record:wrong version number:
../deps/openssl/openssl/ssl/record/ssl3_record.c:355:
Suspicions and Dead Ends
We suspected issues with versions, certificates, and ports, so we checked them all. Upon thorough checks, we confirmed:
- The server supports the required SSL/TLS versions.
- Ports and authentication were correctly configured.
Certificates were valid.
Yet, the issue persisted.
The Revelation
We even created a test script to isolate the bug and experiment with Nodemailer’s configuration.
To connect to the SMTP server, we passed key parameters:
- host
- port
- secure (determining whether authentication credentials—user and pass—are needed).
Normally, we set these values in the .env file and reference them in Nodemailer like:
process.env.<variable_name>
And then it hit us: the secure parameter was passed as a string (“false”)—which was interpreted as true. We recalled an important rule: when loaded from an .env file, all values are strings. Nodemailer automatically converts port to a number, so that wasn’t an issue. But secure always converts to a boolean. Meaning, passing “false” turned into true due to double negation:
this.secureConnection = !!this.options.secure;
(Source: Nodemailer Code)
We didn’t catch this in staging, because the .env file there had secure=true.
After adding proper type handling, everything started working.
Three days of debugging over one tiny detail!..
Lesson learned
The smallest details can cause the biggest headaches. What seemed like a deep SSL issue turned out to be a simple type conversion mistake—easy to miss, but costly to find.