pentaho/pentaho-kettle · critical · HttpException
Unable to get authorization token
Error message
Unable to get authorization token
What it means
In JobEntryMail's getOauthToken, the OAuth2 token endpoint returned a non-200 status, throwing HttpException with the status line, wrapped in RuntimeException. The job could not obtain the bearer token for XOAUTH2 SMTP authentication.
Solutions
- Unwrap the RuntimeException cause to see the HTTP status and correct the OAuth credentials in the job entry.
- Regenerate the refresh token via your provider's OAuth flow and update the job entry.
- Confirm the token endpoint is reachable from the job executor (proxy/firewall/DNS).
- Catch the RuntimeException around getOauthToken and fail with an actionable message.
Example fix
// before redirectUri = "http://localhost:8080/callback"; // not registered with the OAuth app // after redirectUri = "https://app.example.com/oauth2/callback"; // matches registered redirect URI
Defensive patterns
Strategy: try-catch
Validate before calling
if (Utils.isEmpty(oauthUsername) || Utils.isEmpty(authenticationToken)) {
throw new KettleException("OAuth settings incomplete for mail job entry.");
} Try / catch
try { execute(); } catch (RuntimeException e) {
Throwable c = e.getCause();
logError("OAuth token exchange failed; verify credentials/redirect URI/network: " + (c != null ? c.getMessage() : e.getMessage()), e);
} Prevention
- Rotate refresh tokens before expiry and after any consent revocation.
- Match redirect_uri exactly with the OAuth app registration.
- Test token-endpoint reachability from the job executor after infra changes.
When it happens
Trigger: HTTP POST to the token URL returns non-200 (invalid/ expired refresh token, wrong client id/secret/redirect URI) or the token JSON cannot be read (IOException).
Common situations: Expired refresh token after the OAuth app's consent was revoked; mismatched redirect_uri vs the app registration; proxy/firewall blocking the token endpoint from the job server.
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/3fc1b56a29b409ad.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/mail-job/impl/src/main/java/org/pentaho/di/job/entries/mail/JobEntryMail.java:1526
this.tokenUrl = environmentSubstitute( tokenUrl );
HttpPost httpPost = new HttpPost( this.tokenUrl );
List<NameValuePair> form = new ArrayList<>();
form.add( new BasicNameValuePair( "scope", environmentSubstitute( scope ) ) );
form.add( new BasicNameValuePair( "client_id", environmentSubstitute( clientId ) ) );
form.add( new BasicNameValuePair( "client_secret", environmentSubstitute( secretKey ) ) );
form.add( new BasicNameValuePair( "grant_type", environmentSubstitute( grant_type ) ) );
if ( grant_type.equals( JobEntryMail.GRANTTYPE_REFRESH_TOKEN ) ) {
form.add( new BasicNameValuePair( JobEntryMail.GRANTTYPE_REFRESH_TOKEN, environmentSubstitute( refresh_token ) ) );
}
if ( grant_type.equals( JobEntryMail.GRANTTYPE_AUTHORIZATION_CODE ) ) {
form.add( new BasicNameValuePair( "code", environmentSubstitute( authorization_code ) ) );
form.add( new BasicNameValuePair( "redirect_uri", environmentSubstitute( redirectUri ) ) );
}
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 );
}
}
@Override
public List<ResourceReference> getResourceDependencies( JobMeta jobMeta ) {
List<ResourceReference> references = super.getResourceDependencies( jobMeta );
String realServername = jobMeta.environmentSubstitute( server );
ResourceReference reference = new ResourceReference( this );
reference.getEntries().add( new ResourceEntry( realServername, ResourceType.SERVER ) );
references.add( reference );View on GitHub (pinned to f3058517a1)