pentaho/pentaho-kettle · error · HttpException

Unable to get authorization token

Error message

Unable to get authorization token 

What it means

Thrown while MailInputMeta exchanges an authorization code (or credentials) for an OAuth2 access token via HTTP POST. Any non-200 status from the token endpoint raises HttpException, which is rethrown as a RuntimeException, aborting authentication for the Mail Input step.

Solutions

  1. Check the wrapped cause and the status line (response.getStatusLine()) for the provider's error (e.g. 400 invalid_grant)
  2. Verify client ID, client secret, tenant, and redirect_uri exactly match the OAuth app registration
  3. Ensure the environment variables substituted via variables.environmentSubstitute() resolve to correct values
  4. Confirm network/proxy access to login.microsoftonline.com or the configured token endpoint
  5. Regenerate the authorization code — codes are single-use and expire in minutes

Example fix

// before
if ( response.getStatusLine().getStatusCode() != HttpStatus.SC_OK ) {
  throw new HttpException( "Unable to get authorization token " + response.getStatusLine().toString() );
}
// after
if ( response.getStatusLine().getStatusCode() != HttpStatus.SC_OK ) {
  String body = EntityUtils.toString( response.getEntity() );
  throw new HttpException( "Token endpoint returned " + response.getStatusLine()
    + ": " + body );
}
Defensive patterns

Strategy: validation

Validate before calling

boolean tokenConfigValid = clientId != null && !clientId.isEmpty()
  && clientSecret != null && !clientSecret.isEmpty()
  && redirectUri != null && redirectUri.startsWith( "https://" );
if ( !tokenConfigValid ) {
  throw new IllegalArgumentException( "OAuth client id/secret/redirect_uri must be set before token exchange" );
}

Try / catch

try {
  EmailAuthenticationResponse resp = meta.getAuthenticationResponse(...);
} catch ( RuntimeException e ) {
  // inspect cause: HttpException status line or IOException
  logError( "Token exchange failed: " + e.getCause().getMessage() );
}

Prevention

When it happens

Trigger: POSTing the token request (redirect_uri, client id/secret form entity) to the OAuth provider and receiving a status other than 200 — bad client secret, expired/mismatched redirect URI, or unreachable/blocked endpoint. Also on IOException/HttpException during response read.

Common situations: Misconfigured OAuth app (wrong redirect URI or client secret), tenant/admin consent missing in Microsoft 365, network proxy blocking the token endpoint, or using a deactivated client.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/79d614fbe49972dc. Report an issue: GitHub.

Appendix: source

Thrown at plugins/email-messages/impl/src/main/java/org/pentaho/di/trans/steps/mailinput/MailInputMeta.java:1048

    try (CloseableHttpClient client = HttpClientManager.getInstance().createDefaultClient()) {
      HttpPost httpPost = new HttpPost( variables.environmentSubstitute( tokenUrl ) );
      List<NameValuePair> form = new ArrayList<>();
      form.add(new BasicNameValuePair("scope", variables.environmentSubstitute( scope ) ) );
      form.add(new BasicNameValuePair("client_id", variables.environmentSubstitute( clientId ) ));
      form.add(new BasicNameValuePair("client_secret", variables.environmentSubstitute( secretKey ) ) );
      form.add(new BasicNameValuePair("grant_type", grantType));
      if (grantType.equals(GRANTTYPE_REFRESH_TOKEN)) {
        form.add(new BasicNameValuePair(GRANTTYPE_REFRESH_TOKEN, variables.environmentSubstitute( refreshToken ) ) );
      }
      if (grantType.equals(GRANTTYPE_AUTHORIZATION_CODE)) {
        form.add(new BasicNameValuePair("code", variables.environmentSubstitute( authorizationCode ) ) );
        form.add(new BasicNameValuePair("redirect_uri", variables.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);
    }
  }
}

View on GitHub (pinned to f3058517a1)