pentaho/pentaho-kettle · critical · HttpException
Unable to get authorization token " +…
Error message
Unable to get authorization token " + response.getStatusLine().toString()
What it means
In Mail.getOauthToken(), the OAuth2 token endpoint responded with a non-200 status; an HttpException with the full status line is thrown and wrapped in a RuntimeException. This means the client could not obtain the bearer token needed for SMTP XOAUTH2 authentication.
Solutions
- Inspect the wrapped cause in the RuntimeException for the exact HTTP status line and fix credentials (client id, secret, refresh token, redirect URI) in the Mail step's OAuth settings.
- Re-generate the refresh token following your provider's OAuth consent flow (e.g. Google OAuth Playground for Gmail).
- Verify network/proxy reachability of the token endpoint from the Pentaho server.
- Catch the RuntimeException in calling code and surface a clear 'check OAuth configuration' message instead of failing the transformation silently.
Example fix
// before
authenticationToken = "${OAUTH_REFRESH}"; // expired refresh token
// after
authenticationToken = "1//0abc..."; // freshly generated refresh token, redirect_uri matches app registration Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: verify OAuth config fields are non-empty before sending
if (Utils.isEmpty(oauthUsername) || Utils.isEmpty(authenticationToken)) {
throw new KettleException("OAuth settings incomplete: username and refresh token are required.");
} Try / catch
try { sendMail(...); } catch (RuntimeException e) {
Throwable c = e.getCause();
logError("OAuth token acquisition failed (check client id/secret, refresh token, redirect URI): " + (c != null ? c.getMessage() : e.getMessage()), e);
} Prevention
- Refresh OAuth tokens before expiry; schedule a check on refresh-token validity.
- Keep redirect_uri in the Mail step identical to the app registration.
- Verify outbound HTTPS to the token endpoint from the Pentaho server after network changes.
When it happens
Trigger: HTTP POST to the OAuth token URL returns status != 200 (invalid client_id/client_secret/refresh_token, wrong redirect_uri, expired token), or the response body cannot be parsed into EmailAuthenticationResponse (IOException).
Common situations: Expired or revoked refresh token; wrong OAuth tenant/redirect URI configured in the Mail step; network/proxy blocking the token endpoint; Gmail OAuth scope changes after Google policy updates.
Related errors
- Unable to get authorization token
- Unable to get authorization token
- CmsTokenProvider: Keycloak token request failed — HTTP
- Auth error
- HTTP.Exception.Authentication
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/a319852134ce2823.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/mail/impl/src/main/java/org/pentaho/di/trans/steps/mail/Mail.java:790
tokenUrl = environmentSubstitute( meta.getTokenUrl() );
HttpPost httpPost = new HttpPost( tokenUrl );
List<NameValuePair> form = new ArrayList<>();
form.add( new BasicNameValuePair( "scope", environmentSubstitute( meta.getScope() ) ) );
form.add( new BasicNameValuePair( "client_id", environmentSubstitute( meta.getClientId() ) ) );
form.add( new BasicNameValuePair( "client_secret", environmentSubstitute( meta.getSecretKey() ) ) );
form.add( new BasicNameValuePair( "grant_type", environmentSubstitute( meta.getGrant_type() ) ) );
if ( meta.getGrant_type().equals( MailMeta.GRANTTYPE_REFRESH_TOKEN ) ) {
form.add( new BasicNameValuePair( MailMeta.GRANTTYPE_REFRESH_TOKEN, environmentSubstitute( meta.getRefresh_token() ) ) );
}
if ( meta.getGrant_type().equals( MailMeta.GRANTTYPE_AUTHORIZATION_CODE ) ) {
form.add( new BasicNameValuePair( "code", environmentSubstitute( meta.getAuthorization_code() ) ) );
form.add( new BasicNameValuePair( "redirect_uri", environmentSubstitute( meta.getRedirectUri() ) ) );
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity( form, Consts.UTF_8 );
httpPost.setEntity( entity );
try ( CloseableHttpResponse response = client.execute( httpPost ) ) {
if ( response.getStatusLine().getStatusCode() != HttpStatus.SC_OK ) {
throw new HttpException( "Unable to get authorization token " + response.getStatusLine().toString() );
}
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue( EntityUtils.toString( response.getEntity() ), EmailAuthenticationResponse.class );
} catch ( HttpException | IOException e ) {
throw new RuntimeException( e );
}
} catch ( IOException e ) {
throw new RuntimeException( e );
}
}
@VisibleForTesting
void setAttachedFiles( MailMeta meta, Object[] r, LogChannelInterface log ) throws Exception {
if ( meta.isDynamicFilename() || meta.isZipFilenameDynamic() ) {
setAttachedFilesList( r, log );
} else {
setAttachedFilesList( null, log );
}View on GitHub (pinned to f3058517a1)