New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nodemailer EPIPE error #141

Closed
rikkiloades opened this Issue Feb 27, 2013 · 8 comments

Comments

Projects
None yet
2 participants
@rikkiloades

rikkiloades commented Feb 27, 2013

Using nodemailer v0.3.42.

The script below sometimes will finish and complete, but sometimes it will randomly fail at a random point with one of the following errors:

events.js:71 
    throw arguments[1]; // Unhandled 'error' event
                           ^
Error: write EPIPE
    at errnoException (net.js:770:11)
    at Object.afterWrite (net.js:594:19)

It does fail in the same way but with a different file/line number:

stream.js:81
      throw er; // Unhandled stream error in pipe.
            ^
Error: write EPIPE
    at errnoException (net.js:770:11)
    at Object.afterWrite (net.js:594:19)

As far as I understand these EPIPE error occurs because the other end of a connection dropped and then we tried to write to that connection. No error is being passed back up via a throw or callback error argument so there seems to be no way to see what is causing the error.

var nodemailer = require('nodemailer');
var fs = require('fs');

function NewsletterEmail(newsletterGroup, newsletterName)
{
    var folder = '/var/newsletters/' + newsletterGroup + '/' + newsletterName;
    this._html = fs.readFileSync(folder + '/newsletter.html', 'utf-8');
    this._text = fs.readFileSync(folder + '/newsletter.txt', 'utf-8');
}

NewsletterEmail.prototype.getSubject = function() {
    return 'Testing';
}

NewsletterEmail.prototype.buildHTML = function(email) {
    return this._html;
}

NewsletterEmail.prototype.buildText = function(email) {
    return this._text;
}

function NewsletterMailer(fromEmail)
{
    this._from = fromEmail;
    this._transport = nodemailer.createTransport('sendmail');
}

NewsletterMailer.prototype = {
    send: function(email, newsletterEmail, callback) {
        var mailOptions = {
            to: email,
            from: this._from,
            subject: newsletterEmail.getSubject(),
            html: newsletterEmail.buildHTML(email),
            text: newsletterEmail.buildText(email)
        };

        this._transport.sendMail(mailOptions, callback);

    },

    close: function() {
        this._transport.close();
    }
}

function Newsletter()
{
    this._id = 1;
    this.countSent = 0;
    this.emailsToSend = ['email1@example.com', 'email2@example.com', 'email3@example.com', 'email4@example.com', 'email5@example.com', 'email6@example.com'];
}

Newsletter.prototype.send = function() {
    var newsletter = this;

    var newsletterEmail = new NewsletterEmail('company1', '2013-01-24-mynewsleter');
    var mailer = new NewsletterMailer('company@example.com');

    function sendEmail() {
        var email = newsletter.emailsToSend.pop();

        mailer.send(email, newsletterEmail, function(mailerErr) {
            if (mailerErr) {
                console.log('Mailer error: ', mailerErr);
            }

            newsletter.countSent++;

            console.log('progress ' + newsletter.countSent);

            if (newsletter.emailsToSend.length > 0) {
                sendEmail();
            }
            else {
                mailer.close();
                console.log('complete');
            }
        });
    }

    sendEmail();
}

var nl = new Newsletter();
nl.send();

The output of consistently shows the EPIPE errors start to occur when nodemailer is writing the multipart mime boundaries. but it wont always cause the error, only about 35% of the time it happens.

write(8, "------Nodemailer-0.3.42-?=_1-136"..., 131) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=13813, si_uid=0} ---
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=13818, si_status=0, si_utime=0, si_stime=0} ---
write(4, "\1\0\0\0\0\0\0\0", 8)         = 8
rt_sigreturn()                          = -1 EPIPE (Broken pipe)
futex(0x7f039c0008c8, FUTEX_WAKE_PRIVATE, 1) = 1
write(8, "<!DOCTYPE HTML PUBLIC =22-//W3C/"..., 18098) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=13813, si_uid=0} ---
futex(0x7f039c0008c8, FUTEX_WAKE_PRIVATE, 1) = 1
close(8)                                = 0
epoll_wait(3, {{EPOLLIN|EPOLLHUP, {u32=9, u64=4294967305}}, {EPOLLIN|EPOLLHUP, {u32=11, u64=4294967307}}, {EPOLLIN, {u32=4, u64=4294967300}}}, 64, 0) = 3
epoll_ctl(3, EPOLL_CTL_MOD, 9, {EPOLLIN, {u32=9, u64=4294967305}}) = 0
epoll_ctl(3, EPOLL_CTL_MOD, 11, {EPOLLIN, {u32=11, u64=4294967307}}) = 0
read(4, "\1\0\0\0\0\0\0\0", 8)          = 8
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED|WCONTINUED, NULL) = 13818
ioctl(1, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff2d6f4340) = -1 EINVAL (Invalid argument)
fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
write(1, "progress 1\n", 11progress 1
)            = 11
socketpair(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC, 0, [7, 8]) = 0
socketpair(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC, 0, [10, 12]) = 0
socketpair(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC, 0, [13, 14]) = 0
pipe2([15, 16], O_NONBLOCK|O_CLOEXEC)   = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f03a4b35a10) = 13820
close(16)                               = 0
poll([{fd=15, events=POLLIN|POLLHUP}], 1, -1) = 1 ([{fd=15, revents=POLLHUP}])
close(15)                               = 0
close(7)                                = 0
ioctl(8, FIONBIO, [1])                  = 0
close(12)                               = 0
ioctl(10, FIONBIO, [1])                 = 0
close(14)                               = 0
ioctl(13, FIONBIO, [1])                 = 0
wait4(-1, 0x7fff2d6f529c, WNOHANG|WSTOPPED|WCONTINUED, NULL) = 0
futex(0x7f039c0008c8, FUTEX_WAKE_PRIVATE, 1) = 1
brk(0x932000)                           = 0x932000
read(11, "", 65536)                     = 0
close(11)                               = 0
read(9, "", 65536)                      = 0
futex(0x7f039c0008c8, FUTEX_WAKE_PRIVATE, 1) = 1
close(9)                                = 0
write(2, "\n", 1
)                       = 1
write(2, "events.js:71\n", 13events.js:71
)          = 13
write(2, "        throw arguments[1]; // U"..., 55        throw arguments[1]; // Unhandled 'error' event
) = 55
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, " ", 1 )                        = 1
write(2, "^", 1^)                        = 1
write(2, "\n", 1
)                       = 1
futex(0x7f039c0008c8, FUTEX_WAKE_PRIVATE, 1) = 1
write(2, "Error: write EPIPE\n    at errnoE"..., 98Error: write EPIPE
    at errnoException (net.js:770:11)
    at Object.afterWrite (net.js:594:19)
) = 98
exit_group(1)                           = ?
+++ exited with 1 +++

Is there a way error handling could be added for this?

@andris9

This comment has been minimized.

Show comment
Hide comment
@andris9

andris9 Feb 27, 2013

Member

For some reason I'm not able to reproduce EPIPE error with this sample code even if the html and text files are very large. What operation system, node and sendmail version are you using?

Member

andris9 commented Feb 27, 2013

For some reason I'm not able to reproduce EPIPE error with this sample code even if the html and text files are very large. What operation system, node and sendmail version are you using?

@rikkiloades

This comment has been minimized.

Show comment
Hide comment
@rikkiloades

rikkiloades Feb 27, 2013

Operating system: gentoo Linux (amd64 build, kernel 2.6.4)
Node: 0.8.18
Send mail: it's actually postfix with the sendmail integration interface (2.9.5)

On 27 Feb 2013, at 22:41, Andris Reinman notifications@github.com wrote:

For some reason I'm not able to reproduce EPIPE error with this sample code even if the html and text files are very large. What operation system, node and sendmail version are you using?


Reply to this email directly or view it on GitHub.

rikkiloades commented Feb 27, 2013

Operating system: gentoo Linux (amd64 build, kernel 2.6.4)
Node: 0.8.18
Send mail: it's actually postfix with the sendmail integration interface (2.9.5)

On 27 Feb 2013, at 22:41, Andris Reinman notifications@github.com wrote:

For some reason I'm not able to reproduce EPIPE error with this sample code even if the html and text files are very large. What operation system, node and sendmail version are you using?


Reply to this email directly or view it on GitHub.

@rikkiloades

This comment has been minimized.

Show comment
Hide comment
@rikkiloades

rikkiloades Feb 28, 2013

To provide further insight Im pretty sure now that this has something to do with line length wrapping nodemailer is doing. I have my postfix server setup to write mails it receives to the filesystem so i can look at them.

I have reduced the code down to the following:

var nodemailer = require('nodemailer');

var transport = nodemailer.createTransport('Sendmail');

var htmlPart = '';
textPart += '<h1>My Wonderful header</h1>\n';
textPart += '<p>Yes, you read that right. feature x are now supported in Product 2 Edition1.</p>\n';
textPart += '<p>Bring in .lsa, .gif or .txt files and render them at high quality inside Product.</p>\n';

var textPart = '';
textPart += '# My Wonderful header\n';
textPart += '\n';
textPart += 'Yes, you read that right. feature x are now supported in Product 2 Edition1.\n';
textPart += '\n';
textPart += 'Bring in .lsa, .gif or .txt files and render them at high quality inside Product.\n';

var mailOptions = {
    headers: {
        'X-Sender' : 'Company <company@example.com>',
        'X-Mailer' : 'company mailer'
    },
    to: 'person@example.com',
    from: 'company@example.com',
    subject: 'Testing',
    html: htmlPart,
    text: textPart
};

transport.sendMail(mailOptions, function(error, response) {
    if (error) {
        console.log(error);
    }

    console.log('complete');
    transport.close();
});

And this results in the following email file:

Received: from dev-rikki.company.com (odm.company.rikki [127.0.0.1])
        by mail-gateway.company.rikki (Postfix) with ESMTPS id 43E455407E
        for <person@example.com>; Thu, 28 Feb 2013 11:56:38 +0000 (UTC)
Received: by dev-rikki.company.com (Postfix, from userid 0)
        id 391C554086; Thu, 28 Feb 2013 11:56:38 +0000 (UTC)
MIME-Version: 1.0
X-Mailer: Nodemailer (0.3.42; +http://www.nodemailer.com/)
X-Mailer: company mailer
Date: Thu, 28 Feb 2013 11:56:38 GMT
Message-Id: <1362052598195.e3f7b931@Nodemailer>
X-Sender: Company <company@example.com>
From: company@example.com
To: person@example.com
Subject: Testing
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

# My Wonderful header

Yes, you read that right. feature x are now supported in Product 2 Edition1=

Only the text part of the email is being sent out and its getting cut off on a line which is being wrapped to cause only a dot (.) to be on the next line.

It is my understanding that a client of sendmail/SMTP tells it that it has finished writing to the socket by sending a line with only a dot (.) on it. In this case the wrapping happening is causing the dot on the end of that line to be wrapped onto a line by itself and therefore causing the socket to be closed prematurely.

The strace output in my original post backs this up by showing any writes after the text result in the EPIPE error.

This could also be causing the problem with further calls to the sendEmail function in nodemailer as the socket it was using has been closed.

If you change this one line of the source:

textPart += '<p>Yes, you read that right. feature x are now supported in Product 2 Edition1.</p>\n';

To add a newline before Edition1. the full output is now in the file and it does not causes a EPIPE error.

Should nodemailer not handle escaping special character sequences it accidentally generates as a result of the wrapping?

rikkiloades commented Feb 28, 2013

To provide further insight Im pretty sure now that this has something to do with line length wrapping nodemailer is doing. I have my postfix server setup to write mails it receives to the filesystem so i can look at them.

I have reduced the code down to the following:

var nodemailer = require('nodemailer');

var transport = nodemailer.createTransport('Sendmail');

var htmlPart = '';
textPart += '<h1>My Wonderful header</h1>\n';
textPart += '<p>Yes, you read that right. feature x are now supported in Product 2 Edition1.</p>\n';
textPart += '<p>Bring in .lsa, .gif or .txt files and render them at high quality inside Product.</p>\n';

var textPart = '';
textPart += '# My Wonderful header\n';
textPart += '\n';
textPart += 'Yes, you read that right. feature x are now supported in Product 2 Edition1.\n';
textPart += '\n';
textPart += 'Bring in .lsa, .gif or .txt files and render them at high quality inside Product.\n';

var mailOptions = {
    headers: {
        'X-Sender' : 'Company <company@example.com>',
        'X-Mailer' : 'company mailer'
    },
    to: 'person@example.com',
    from: 'company@example.com',
    subject: 'Testing',
    html: htmlPart,
    text: textPart
};

transport.sendMail(mailOptions, function(error, response) {
    if (error) {
        console.log(error);
    }

    console.log('complete');
    transport.close();
});

And this results in the following email file:

Received: from dev-rikki.company.com (odm.company.rikki [127.0.0.1])
        by mail-gateway.company.rikki (Postfix) with ESMTPS id 43E455407E
        for <person@example.com>; Thu, 28 Feb 2013 11:56:38 +0000 (UTC)
Received: by dev-rikki.company.com (Postfix, from userid 0)
        id 391C554086; Thu, 28 Feb 2013 11:56:38 +0000 (UTC)
MIME-Version: 1.0
X-Mailer: Nodemailer (0.3.42; +http://www.nodemailer.com/)
X-Mailer: company mailer
Date: Thu, 28 Feb 2013 11:56:38 GMT
Message-Id: <1362052598195.e3f7b931@Nodemailer>
X-Sender: Company <company@example.com>
From: company@example.com
To: person@example.com
Subject: Testing
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

# My Wonderful header

Yes, you read that right. feature x are now supported in Product 2 Edition1=

Only the text part of the email is being sent out and its getting cut off on a line which is being wrapped to cause only a dot (.) to be on the next line.

It is my understanding that a client of sendmail/SMTP tells it that it has finished writing to the socket by sending a line with only a dot (.) on it. In this case the wrapping happening is causing the dot on the end of that line to be wrapped onto a line by itself and therefore causing the socket to be closed prematurely.

The strace output in my original post backs this up by showing any writes after the text result in the EPIPE error.

This could also be causing the problem with further calls to the sendEmail function in nodemailer as the socket it was using has been closed.

If you change this one line of the source:

textPart += '<p>Yes, you read that right. feature x are now supported in Product 2 Edition1.</p>\n';

To add a newline before Edition1. the full output is now in the file and it does not causes a EPIPE error.

Should nodemailer not handle escaping special character sequences it accidentally generates as a result of the wrapping?

@andris9

This comment has been minimized.

Show comment
Hide comment
@andris9

andris9 Feb 28, 2013

Member

Oh, now i get it. You are totally right, sendmail wants the single dot to be SMTP encoded or -i flag to keep the dots, I always thought that keeping the dots is the default behavior.

Member

andris9 commented Feb 28, 2013

Oh, now i get it. You are totally right, sendmail wants the single dot to be SMTP encoded or -i flag to keep the dots, I always thought that keeping the dots is the default behavior.

@andris9

This comment has been minimized.

Show comment
Hide comment
@andris9

andris9 Feb 28, 2013

Member

I updated the code to include -i flag with sendmail. From the sendmail manual:

-i Ignore dots alone on lines by themselves in incoming mes-
   sages.  This should be set if you are reading data from a
   file.
Member

andris9 commented Feb 28, 2013

I updated the code to include -i flag with sendmail. From the sendmail manual:

-i Ignore dots alone on lines by themselves in incoming mes-
   sages.  This should be set if you are reading data from a
   file.

@andris9 andris9 closed this Feb 28, 2013

@andris9

This comment has been minimized.

Show comment
Hide comment
@andris9

andris9 Feb 28, 2013

Member

Here's the actual commit: 738fd88

Member

andris9 commented Feb 28, 2013

Here's the actual commit: 738fd88

@rikkiloades

This comment has been minimized.

Show comment
Hide comment
@rikkiloades

rikkiloades Feb 28, 2013

Thank you for fixing this in the core library so quickly.

rikkiloades commented Feb 28, 2013

Thank you for fixing this in the core library so quickly.

@andris9

This comment has been minimized.

Show comment
Hide comment
@andris9

andris9 Feb 28, 2013

Member

no problems 😎

Member

andris9 commented Feb 28, 2013

no problems 😎

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment