pentaho/pentaho-kettle · error · JavaScriptException
sendMail
Error message
sendMail: {e.toString()} What it means
Thrown by the sendMail JavaScript function when the underlying javax.mail send fails. After building a MimeMessage (SMTP host, from, recipients, subject, body) the function calls Transport.send(msg); any MessagingException or address problem is caught and re-thrown as 'sendMail: ' + e.toString(), so the text after the prefix contains the real Java exception (e.g. UnknownHostException, AuthenticationFailedException).
Solutions
- Read the text after 'sendMail: ' — it names the underlying Java exception; fix accordingly.
- Verify the SMTP hostname and that the port is reachable (telnet host 25) from the Kettle server.
- If the server requires auth or TLS, use the Mail step or add mail.smtp.auth/mail.smtp.starttls session properties instead of sendMail().
- Validate recipient address syntax before calling; strip whitespace and semicolons not accepted by InternetAddress.
Example fix
// before (host unreachable)
sendMail("smtp.wronghost.local", from, to, subject, body)
// after (verify first)
if ( smtpHost != null && smtpHost.indexOf(".") >= 0 ) sendMail(smtpHost, from, to, subject, body) Defensive patterns
Strategy: try-catch
Validate before calling
// JS
function safeSendMail(host, from, to, subj, body) {
if (!host || !from || !to || subj == null || body == null) return false;
try { sendMail(host, from, to, subj, body); return true; } catch (e) { return false; }
} Type guard
function canSendMail(args) { return args.length === 5 && args.every(function(a){ return a != null; }); } Try / catch
try { sendMail(host, from, to, subj, body); } catch (e) { logError("sendMail failed: " + e); } Prevention
- Parse the text after 'sendMail: ' — it names the real Java exception
- Verify SMTP host/port reachability from the Kettle server
- Use the Mail job entry when SMTP auth/TLS is required
- Validate recipient address syntax before sending
When it happens
Trigger: Calling sendMail(smtpHost, from, recipients, subject, body) where the SMTP host is unresolvable, the server rejects the connection or authentication, a recipient address is malformed, or the mail session cannot be established.
Common situations: Wrong SMTP host/port in a Kettle job's JavaScript step; corporate firewall blocking port 25; unauthenticated relaying now required (550 relay denied); typo'd recipient addresses; missing mail.jar/activation dependencies in older setups.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- sendMail: " + e.toString()
- JobGetMailsFromPOP.Error.Connecting
- JobGetMailsFromPOP.Error.NewConnection
- JobMail.Error.ReplyEmailNotFilled
- Mail.Error.ReplyEmailNotFilled
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/8a166808b8c3b52a.
Report an issue: GitHub.
Appendix: source
Thrown at engine/src/main/java/org/pentaho/di/trans/steps/scriptvalues_mod/ScriptValuesAddedFunctions.java:1279
// Get Recipients
String[] strArrRecipients = ( (String) ArgList[2] ).split( "," );
InternetAddress[] addressTo = new InternetAddress[strArrRecipients.length];
for ( int i = 0; i < strArrRecipients.length; i++ ) {
addressTo[i] = new InternetAddress( strArrRecipients[i] );
}
msg.setRecipients( Message.RecipientType.TO, addressTo );
// Optional : You can also set your custom headers in the Email if you Want
msg.addHeader( "MyHeaderName", "myHeaderValue" );
// Setting the Subject and Content Type
msg.setSubject( (String) ArgList[3] );
msg.setContent( ArgList[4], "text/plain" );
Transport.send( msg );
} catch ( Exception e ) {
throw Context.reportRuntimeError( "sendMail: " + e.toString() );
}
} else {
throw Context.reportRuntimeError( "The function call sendMail requires 5 arguments." );
}
}
public static String upper( Context actualContext, Scriptable actualObject, Object[] ArgList,
Function FunctionContext ) {
String sRC = "";
if ( ArgList.length == 1 ) {
try {
if ( isNull( ArgList[0] ) ) {
return null;
} else if ( isUndefined( ArgList[0] ) ) {
return (String) Context.getUndefinedValue();
}
sRC = Context.toString( ArgList[0] );
sRC = sRC.toUpperCase();View on GitHub (pinned to f3058517a1)