DEV Community

Cover image for Sherlock Holmes: The Case of the Invalid HeloHost Error
Boopathi
Boopathi

Posted on • Originally published at programmerraja.is-a.dev

3 2 2 2 2

Sherlock Holmes: The Case of the Invalid HeloHost Error

This time, our debugging adventure takes us deep into the world of SMTP, where an elusive Invalid HELO error threatened to derail email sending. Let’s dive in!

The Mystery Begins

It was a Friday evening, and I was ready to log off and enjoy the weekend. But just as I was packing up, the customer-facing team reported an issue: a user couldn’t send emails via SMTP from our platform.

Sighing, I pulled out my laptop, checked the logs, and found this cryptic error:

550 Requested action not taken: Invalid HeloHost (#5.5.0)
"responseCode":550, "command":"MAIL FROM"
Enter fullscreen mode Exit fullscreen mode

Not exactly the “Have a great weekend!” message I was hoping for.

The First Hypothesis

This was a new error for me, so I did some quick Googling. My first suspicion? The user might have IP restrictions on their SMTP server.

We asked them to whitelist our server’s IP, hoping that would fix it. But when we retried, the error persisted. Time for a deeper dive.

Digging Deeper

Things rarely work as expected on the first try. So, I turned to Google and ChatGPT for insights. Here’s what I learned:

  • Error Code 550: A permanent failure response.
  • Message Meaning: The recipient’s mail server rejected the MAIL FROM command due to an issue with the HELO/EHLO handshake.
  • Potential Fix: Ensure the sending server uses a fully qualified domain name (FQDN) in the HELO/EHLO command (e.g., EHLO mymailserver.example.com).

Understanding the SMTP Handshake

To get a better grasp of the issue, I reviewed the SMTP handshake process:

SMTP handshake

  1. Client Introduces Itself (EHLO/HELO)
    • The sender’s server initiates the connection.
  2. Server Responds
    • The recipient’s server acknowledges and lists supported features.
  3. Authentication (If Required)
    • If needed, credentials are exchanged.
  4. Email Transaction Begins
    • The sender specifies MAIL FROM and RCPT TO.
    • The email content is sent (DATA command).
  5. Server Confirms & Closes Connection
    • The recipient server acknowledges (250 OK).
    • The sender closes the connection (QUIT command).

More details? Check the official spec here.

The Breakthrough

Armed with this knowledge, I dug into our SMTP implementation. We were using Nodemailer, so I checked how it sends the EHLO command.

Inside the _actionGreeting function, I found this:

this._sendCommand('EHLO ' + this.name);
Enter fullscreen mode Exit fullscreen mode

Then, I traced how this.name is set:

this.name = this.options.name || this._getHostname();
Enter fullscreen mode Exit fullscreen mode

Since we hadn’t explicitly set options.name, it defaulted to _getHostname(), which is defined as:

_getHostname() {
    let defaultHostname;
    try {
        defaultHostname = os.hostname() || '';
    } catch (err) {
        defaultHostname = 'localhost';
    }
    if (!defaultHostname || !defaultHostname.includes('.')) {
        defaultHostname = '[127.0.0.1]';
    }
    if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(defaultHostname)) {
        defaultHostname = `[${defaultHostname}]`;
    }
    return defaultHostname;
}
Enter fullscreen mode Exit fullscreen mode

The Culprit: Private IP in EHLO

Our server was running inside a Kubernetes pod, where _getHostname() returned the pod’s private IP.

According to RFC 5321, SMTP servers may verify the domain name in the EHLO command against the client’s IP, but they must not reject a message solely on a failed verification:

An SMTP server MAY verify that the domain name argument in the EHLO  
command actually corresponds to the IP address of the client.  
However, if the verification fails, the server MUST NOT refuse to  
accept a message on that basis.  
Enter fullscreen mode Exit fullscreen mode

However, for some reason, this particular customer’s server was rejecting the handshake, leading to the Invalid HeloHost error.

The Fix

To resolve the issue, we explicitly set a public hostname for EHLO by passing a valid domain when initializing Nodemailer:

const transporter = nodemailer.createTransport({
    host: "smtp.example.com",
    port: 587,
    secure: false,
    auth: {
        user: "user@example.com",
        pass: "password"
    },
    name: "public.hostname.com"  // <-- Fix: Use a valid hostname
});
Enter fullscreen mode Exit fullscreen mode

With this change, the SMTP connection worked flawlessly, and emails started sending without any issues!

Case Closed

Did it solve the problem? Yes but it also introduced a new one! I'll unravel that mystery in the next post.

Found this helpful? Clap 👏 and follow for more debugging adventures!

Quadratic AI

Quadratic AI – The Spreadsheet with AI, Code, and Connections

  • AI-Powered Insights: Ask questions in plain English and get instant visualizations
  • Multi-Language Support: Seamlessly switch between Python, SQL, and JavaScript in one workspace
  • Zero Setup Required: Connect to databases or drag-and-drop files straight from your browser
  • Live Collaboration: Work together in real-time, no matter where your team is located
  • Beyond Formulas: Tackle complex analysis that traditional spreadsheets can't handle

Get started for free.

Watch The Demo 📊✨

Top comments (0)

Jetbrains Survey

Calling all developers!

Participate in the Developer Ecosystem Survey 2025 and get the chance to win a MacBook Pro, an iPhone 16, or other exciting prizes. Contribute to our research on the development landscape.

Take the survey

AWS Security LIVE!

Hosted by security experts, AWS Security LIVE! showcases AWS Partners tackling real-world security challenges. Join live and get your security questions answered.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️