{"record":{"id":"3fc1b56a29b409ad","repo":"pentaho/pentaho-kettle","slug":"unable-to-get-authorization-token-jobentrymail","errorCode":null,"errorMessage":"Unable to get authorization token ","messagePattern":"Unable to get authorization token ","errorType":"exception","errorClass":"HttpException","httpStatus":null,"severity":"critical","filePath":"plugins/mail-job/impl/src/main/java/org/pentaho/di/job/entries/mail/JobEntryMail.java","lineNumber":1526,"sourceCode":"      this.tokenUrl = environmentSubstitute( tokenUrl );\n      HttpPost httpPost = new HttpPost( this.tokenUrl );\n      List<NameValuePair> form = new ArrayList<>();\n      form.add( new BasicNameValuePair( \"scope\", environmentSubstitute( scope ) ) );\n      form.add( new BasicNameValuePair( \"client_id\", environmentSubstitute( clientId ) ) );\n      form.add( new BasicNameValuePair( \"client_secret\", environmentSubstitute( secretKey ) ) );\n      form.add( new BasicNameValuePair( \"grant_type\", environmentSubstitute( grant_type ) ) );\n      if ( grant_type.equals( JobEntryMail.GRANTTYPE_REFRESH_TOKEN ) ) {\n        form.add( new BasicNameValuePair( JobEntryMail.GRANTTYPE_REFRESH_TOKEN, environmentSubstitute( refresh_token ) ) );\n      }\n      if ( grant_type.equals( JobEntryMail.GRANTTYPE_AUTHORIZATION_CODE ) ) {\n        form.add( new BasicNameValuePair( \"code\", environmentSubstitute( authorization_code ) ) );\n        form.add( new BasicNameValuePair( \"redirect_uri\", environmentSubstitute( redirectUri ) ) );\n      }\n      UrlEncodedFormEntity entity = new UrlEncodedFormEntity( form, Consts.UTF_8 );\n      httpPost.setEntity( entity );\n      try ( CloseableHttpResponse response = client.execute( httpPost ) ) {\n        if ( response.getStatusLine().getStatusCode() != HttpStatus.SC_OK ) {\n          throw new HttpException( \"Unable to get authorization token \" + response.getStatusLine().toString() );\n        }\n        ObjectMapper mapper = new ObjectMapper();\n        return mapper.readValue( EntityUtils.toString( response.getEntity() ), EmailAuthenticationResponse.class );\n      } catch ( HttpException | IOException e ) {\n        throw new RuntimeException( e );\n      }\n    } catch ( IOException e ) {\n      throw new RuntimeException( e );\n    }\n  }\n\n  @Override\n  public List<ResourceReference> getResourceDependencies( JobMeta jobMeta ) {\n    List<ResourceReference> references = super.getResourceDependencies( jobMeta );\n    String realServername = jobMeta.environmentSubstitute( server );\n    ResourceReference reference = new ResourceReference( this );\n    reference.getEntries().add( new ResourceEntry( realServername, ResourceType.SERVER ) );\n    references.add( reference );","sourceCodeStart":1508,"sourceCodeEnd":1544,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/plugins/mail-job/impl/src/main/java/org/pentaho/di/job/entries/mail/JobEntryMail.java#L1508-L1544","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nredirectUri = \"http://localhost:8080/callback\"; // not registered with the OAuth app\n// after\nredirectUri = \"https://app.example.com/oauth2/callback\"; // matches registered redirect URI","handlingStrategy":"try-catch","validationCode":"if (Utils.isEmpty(oauthUsername) || Utils.isEmpty(authenticationToken)) {\n  throw new KettleException(\"OAuth settings incomplete for mail job entry.\");\n}","typeGuard":null,"tryCatchPattern":"try { execute(); } catch (RuntimeException e) {\n  Throwable c = e.getCause();\n  logError(\"OAuth token exchange failed; verify credentials/redirect URI/network: \" + (c != null ? c.getMessage() : e.getMessage()), e);\n}","preventionTips":["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."],"tags":["oauth","email","http","authentication"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}