Expected response code 220 but got code "" with message ""

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Decoding Laravel Mail Errors: Why You Get 220 but an Empty Message

As senior developers, we spend a significant amount of time debugging external service interactions. When dealing with email delivery in a framework like Laravel, cryptic error messages can be incredibly frustrating. The scenario you’ve described—receiving a response code like 220 but an empty message body ("")—points directly to a disconnect between the application's expectation and the external mail server's actual response.

This post will break down why this happens, analyze your specific configuration, and guide you toward resolving these elusive mail delivery errors in your Laravel application.

Understanding the Response Code Discrepancy

The HTTP response code 220 is typically associated with successful SMTP communication (e.g., "Service ready for new mail input"). However, receiving this code without a meaningful message suggests that while the connection was established, the subsequent command or data transfer failed internally on the mail server side, or perhaps Laravel failed to correctly parse the reply.

In 90% of these cases, the problem lies not in your Laravel code structure itself, but in the SMTP configuration, authentication, or access permissions—especially when dealing with services like Gmail, which have increasingly strict security protocols. Caching commands (php artisan cache:clear and config:cache) are essential for deployment consistency, but they rarely fix fundamental credential issues.

Analyzing Your Laravel Mail Configuration

Let's review the configuration you provided, focusing on the mail driver setup:

MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=username@gmail.com
MAIL_PASSWORD=password
MAIL_ENCRYPTION=ssl

While this looks structurally correct for an SMTP setup, the failure often stems from one of these points:

  1. Authentication Failure: The most common cause is that the MAIL_USERNAME and MAIL_PASSWORD are rejected by the server. For services like Gmail, using standard account passwords directly is often blocked. You usually need to generate an App Password specifically for application access, which bypasses standard account security restrictions. If authentication fails silently, the mail delivery process terminates without a useful error message being returned to Laravel.
  2. TLS/Encryption Mismatch: Ensure that the MAIL_ENCRYPTION setting (ssl or tls) precisely matches what your SMTP host requires. While ssl is often interchangeable with tls, strict adherence to the server's requirements is vital.

As a reminder, robust application architecture, much like in Laravel where we rely on services, demands that we validate external dependencies rigorously. Think about how this relates to building resilient systems, similar to the principles discussed at https://laravelcompany.com regarding dependency management and service contracts.

The from Address Conflict: A Deeper Look

You raised a valid concern about setting the 'from' address dynamically within the mail callback:

Mail::send('emails.contact', $data, function($message) use ($data) {
    $message->from($data['email']); // Dynamic 'from' address
    $message->to('username@gmail.com');
    $message->subject($data['subject']);
});

Your intuition is correct. While Laravel allows dynamic setting of the from address, conflicts can arise if you mix this dynamic setting with default configurations defined in config/mail.php. The documentation implies that the global 'from' should be stable unless explicitly overridden elsewhere.

In this specific scenario, where you are sending a transactional email:

  1. The Conflict: If the server (e.g., Gmail) rejects the sender address because it doesn't match the authenticated account credentials, the failure occurs regardless of how Laravel structures the request. The error is at the SMTP handshake level, not the Laravel formatting level.
  2. Best Practice: For transactional emails, it is often safer to define a static sender in config/mail.php (like MAIL_FROM_ADDRESS) and only override it when necessary. However, since you are sending an email from the user's provided address, this dynamic assignment should work if the authentication works flawlessly.

Actionable Troubleshooting Steps

Before diving back into code changes, follow these steps to isolate the issue:

  1. Verify Credentials: Log into your SMTP provider's security settings and confirm that the username and password you are using have permission to send mail via SMTP. If using Gmail, ensure you are using an App Password.
  2. Test Manually: Try sending a simple test email directly through a tool like Postman or a dedicated email client configured with the exact same SMTP settings (host, port, username, password). If it fails there, the issue is 100% external to Laravel.
  3. Check Mail Logs: If you are using a system-level mail agent (like Sendmail), check the system logs for detailed SMTP error messages that might be suppressed from Laravel's view layer.

By systematically checking the external dependency (the SMTP server) before optimizing the application logic, you will resolve these frustrating errors and ensure reliable email delivery in your Laravel application.