pentaho/pentaho-kettle · error · HttpException

Unable to get authorization token

Error message

Unable to get authorization token <statusLine>

What it means

The job entry's OAuth2 token retrieval (used when authentication type is OAuth) POSTs credentials to the configured token URL. If the HTTP response status is not 200 OK, it throws HttpException 'Unable to get authorization token <statusLine>'. This means the identity provider refused or failed the token exchange before any mail could be fetched.

Solutions

  1. Read the status line in the message (e.g. 400/401) and check the IdP/token-endpoint logs for the error body; fix the offending credential or request parameter.
  2. Verify the token URL, client id, client secret, and redirect_uri in the job entry match the OAuth app registration exactly.
  3. Regenerate or refresh the expired credential/refresh token and re-enter it in the job entry.
  4. Confirm network/proxy access to the token endpoint (test with curl -v against the token URL).

Example fix

// before: wrong endpoint
POST https://login.example.com/token  -> 404
// after: correct tenant endpoint
POST https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token -> 200
Defensive patterns

Strategy: try-catch

Validate before calling

int status = probeTokenEndpoint(tokenUrl); // pre-flight HEAD/POST check
if (status != 200) { logError("Token endpoint not healthy, HTTP " + status); return; }

Try / catch

try { executeJobEntry(); } catch (RuntimeException e) {
  if (e.getCause() instanceof HttpException && e.getMessage().contains("authorization token")) {
    logError("OAuth token exchange failed: " + e.getCause().getMessage() + " — check client id/secret, redirect_uri, token URL");
  } else { throw e; }
}

Prevention

When it happens

Trigger: getOauthToken(tokenUrl) executes the POST and response.getStatusLine().getStatusCode() != 200 — bad client id/secret, expired or revoked refresh token, wrong redirect_uri, wrong token endpoint URL, or the IdP returning 400/401/403/5xx.

Common situations: Expired OAuth client secret; redirect URI mismatch with the registered app; token URL typo or pointing to the wrong tenant; IdP outage returning 500/503; clock skew invalidating the grant; scopes not approved for the account.

Related errors


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

Appendix: source

Thrown at plugins/email-messages/impl/src/main/java/org/pentaho/di/job/entries/getpop/JobEntryGetPOP.java:1535

      HttpPost httpPost = new HttpPost( parentJobMeta.environmentSubstitute( tokenUrl ) );
      List<NameValuePair> form = new ArrayList<>();
      form.add( new BasicNameValuePair( "scope", parentJobMeta.environmentSubstitute( getScope() ) ) );
      form.add( new BasicNameValuePair( "client_id", parentJobMeta.environmentSubstitute( getClientId() ) ));
      form.add( new BasicNameValuePair( "client_secret", parentJobMeta.environmentSubstitute( getSecretKey() ) ));
      String realGrantType = parentJobMeta.environmentSubstitute( getGrant_type() );
      form.add( new BasicNameValuePair( "grant_type", realGrantType ) );
      if ( realGrantType.equals( JobEntryGetPOP.GRANTTYPE_REFRESH_TOKEN ) ) {
        form.add( new BasicNameValuePair( JobEntryGetPOP.GRANTTYPE_REFRESH_TOKEN, parentJobMeta.environmentSubstitute( getRefresh_token() ) ) );
      }
      if ( realGrantType.equals( JobEntryGetPOP.GRANTTYPE_AUTHORIZATION_CODE ) ) {
        form.add( new BasicNameValuePair( "code", parentJobMeta.environmentSubstitute( getAuthorization_code() ) ) );
        form.add( new BasicNameValuePair( "redirect_uri", parentJobMeta.environmentSubstitute( 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() );
        }
        String responseBody = EntityUtils.toString( response.getEntity() );
        ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue( responseBody, 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 );
    if ( !Utils.isEmpty( serverName ) ) {
      String realServername = jobMeta.environmentSubstitute( serverName );
      ResourceReference reference = new ResourceReference( this );

View on GitHub (pinned to f3058517a1)