Skip to content

Commit 0f3068a

Browse files
committed
fix: leverage Nodemailer error codes for better retry logic and UI messages
Add ENOAUTH and EOAUTH2 to SMTP error descriptions so OAuth2 failures produce proper status objects. Discard non-retryable errors (EAUTH, ENOAUTH, EOAUTH2, ETLS, EENVELOPE, EMESSAGE, EPROTOCOL) immediately in the submit worker instead of retrying up to 10 times. Extend SMTP verification switch statements with human-readable messages for six additional error codes.
1 parent b77bda0 commit 0f3068a

File tree

5 files changed

+70
-2
lines changed

5 files changed

+70
-2
lines changed

lib/email-client/message-builder.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,9 @@ const SMTP_ERROR_DESCRIPTIONS = {
467467
EDNS: settings => `EmailEngine failed to resolve DNS record for ${settings.host}`,
468468
ECONNECTION: settings => `EmailEngine failed to establish TCP connection against ${settings.host}`,
469469
EPROTOCOL: settings => `Unexpected response from ${settings.host}`,
470-
EAUTH: () => 'Authentication failed'
470+
EAUTH: () => 'Authentication failed',
471+
ENOAUTH: () => 'Authentication credentials were not provided',
472+
EOAUTH2: () => 'OAuth2 token generation or refresh failed'
471473
};
472474

473475
/**

lib/routes-ui.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4937,11 +4937,29 @@ ${Buffer.from(data.content, 'base64url').toString('base64')}
49374937
case 'EAUTH':
49384938
verifyResult.smtp.error = request.app.gt.gettext('Invalid username or password');
49394939
break;
4940+
case 'ENOAUTH':
4941+
verifyResult.smtp.error = request.app.gt.gettext('Authentication credentials were not provided');
4942+
break;
4943+
case 'EOAUTH2':
4944+
verifyResult.smtp.error = request.app.gt.gettext('OAuth2 authentication failed');
4945+
break;
4946+
case 'ETLS':
4947+
verifyResult.smtp.error = request.app.gt.gettext('TLS protocol error');
4948+
break;
49404949
case 'ESOCKET':
49414950
if (/openssl/.test(verifyResult.smtp.error)) {
49424951
verifyResult.smtp.error = request.app.gt.gettext('TLS protocol error');
49434952
}
49444953
break;
4954+
case 'ETIMEDOUT':
4955+
verifyResult.smtp.error = request.app.gt.gettext('Connection timed out');
4956+
break;
4957+
case 'ECONNECTION':
4958+
verifyResult.smtp.error = request.app.gt.gettext('Could not connect to server');
4959+
break;
4960+
case 'EPROTOCOL':
4961+
verifyResult.smtp.error = request.app.gt.gettext('Unexpected server response');
4962+
break;
49454963
}
49464964
}
49474965
}

lib/ui-routes/account-routes.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,11 +819,29 @@ function init(args) {
819819
case 'EAUTH':
820820
verifyResult.smtp.error = request.app.gt.gettext('Invalid username or password');
821821
break;
822+
case 'ENOAUTH':
823+
verifyResult.smtp.error = request.app.gt.gettext('Authentication credentials were not provided');
824+
break;
825+
case 'EOAUTH2':
826+
verifyResult.smtp.error = request.app.gt.gettext('OAuth2 authentication failed');
827+
break;
828+
case 'ETLS':
829+
verifyResult.smtp.error = request.app.gt.gettext('TLS protocol error');
830+
break;
822831
case 'ESOCKET':
823832
if (/openssl/.test(verifyResult.smtp.error)) {
824833
verifyResult.smtp.error = request.app.gt.gettext('TLS protocol error');
825834
}
826835
break;
836+
case 'ETIMEDOUT':
837+
verifyResult.smtp.error = request.app.gt.gettext('Connection timed out');
838+
break;
839+
case 'ECONNECTION':
840+
verifyResult.smtp.error = request.app.gt.gettext('Could not connect to server');
841+
break;
842+
case 'EPROTOCOL':
843+
verifyResult.smtp.error = request.app.gt.gettext('Unexpected server response');
844+
break;
827845
}
828846
}
829847
}

lib/ui-routes/admin-entities-routes.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2022,11 +2022,29 @@ return payload;`)
20222022
case 'EAUTH':
20232023
verifyResult.smtp.error = request.app.gt.gettext('Invalid username or password');
20242024
break;
2025+
case 'ENOAUTH':
2026+
verifyResult.smtp.error = request.app.gt.gettext('Authentication credentials were not provided');
2027+
break;
2028+
case 'EOAUTH2':
2029+
verifyResult.smtp.error = request.app.gt.gettext('OAuth2 authentication failed');
2030+
break;
2031+
case 'ETLS':
2032+
verifyResult.smtp.error = request.app.gt.gettext('TLS protocol error');
2033+
break;
20252034
case 'ESOCKET':
20262035
if (/openssl/.test(verifyResult.smtp.error)) {
20272036
verifyResult.smtp.error = request.app.gt.gettext('TLS protocol error');
20282037
}
20292038
break;
2039+
case 'ETIMEDOUT':
2040+
verifyResult.smtp.error = request.app.gt.gettext('Connection timed out');
2041+
break;
2042+
case 'ECONNECTION':
2043+
verifyResult.smtp.error = request.app.gt.gettext('Could not connect to server');
2044+
break;
2045+
case 'EPROTOCOL':
2046+
verifyResult.smtp.error = request.app.gt.gettext('Unexpected server response');
2047+
break;
20302048
}
20312049
}
20322050
}

workers/submit.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,19 @@ const submitWorker = new Worker(
295295
// ignore
296296
}
297297

298-
if (err.statusCode >= 500 && err.statusCode !== 503 && job.attemptsMade < job.opts.attempts) {
298+
const NON_RETRYABLE_CODES = new Set([
299+
'EAUTH', // authentication failed
300+
'ENOAUTH', // no credentials provided
301+
'EOAUTH2', // OAuth2 token failure
302+
'ETLS', // TLS handshake failed
303+
'EENVELOPE', // invalid sender/recipients
304+
'EMESSAGE', // message content error
305+
'EPROTOCOL' // SMTP protocol mismatch
306+
]);
307+
308+
const isPermanentSmtp = err.statusCode >= 500 && err.statusCode !== 503;
309+
const isPermanentCode = NON_RETRYABLE_CODES.has(err.code);
310+
if ((isPermanentSmtp || isPermanentCode) && job.attemptsMade < job.opts.attempts) {
299311
try {
300312
// do not retry after 5xx error (except 503 which is transient)
301313
await job.discard();

0 commit comments

Comments
 (0)