{"record":{"id":"a319852134ce2823","repo":"pentaho/pentaho-kettle","slug":"unable-to-get-authorization-token-response-getstatusline","errorCode":null,"errorMessage":"Unable to get authorization token \" + response.getStatusLine().toString()","messagePattern":"Unable to get authorization token \" \\+ response\\.getStatusLine\\(\\)\\.toString\\(\\)","errorType":"http","errorClass":"HttpException","httpStatus":null,"severity":"critical","filePath":"plugins/mail/impl/src/main/java/org/pentaho/di/trans/steps/mail/Mail.java","lineNumber":790,"sourceCode":"      tokenUrl = environmentSubstitute( meta.getTokenUrl() );\n      HttpPost httpPost = new HttpPost( tokenUrl );\n      List<NameValuePair> form = new ArrayList<>();\n      form.add( new BasicNameValuePair( \"scope\", environmentSubstitute( meta.getScope() ) ) );\n      form.add( new BasicNameValuePair( \"client_id\", environmentSubstitute( meta.getClientId() ) ) );\n      form.add( new BasicNameValuePair( \"client_secret\", environmentSubstitute( meta.getSecretKey() ) ) );\n      form.add( new BasicNameValuePair( \"grant_type\", environmentSubstitute( meta.getGrant_type() ) ) );\n      if ( meta.getGrant_type().equals( MailMeta.GRANTTYPE_REFRESH_TOKEN ) ) {\n        form.add( new BasicNameValuePair( MailMeta.GRANTTYPE_REFRESH_TOKEN, environmentSubstitute( meta.getRefresh_token() ) ) );\n      }\n      if ( meta.getGrant_type().equals( MailMeta.GRANTTYPE_AUTHORIZATION_CODE ) ) {\n        form.add( new BasicNameValuePair( \"code\", environmentSubstitute( meta.getAuthorization_code() ) ) );\n        form.add( new BasicNameValuePair( \"redirect_uri\", environmentSubstitute( meta.getRedirectUri() ) ) );\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  @VisibleForTesting\n  void setAttachedFiles( MailMeta meta, Object[] r, LogChannelInterface log ) throws Exception {\n    if ( meta.isDynamicFilename() || meta.isZipFilenameDynamic() ) {\n      setAttachedFilesList( r, log );\n    } else {\n      setAttachedFilesList( null, log );\n    }","sourceCodeStart":772,"sourceCodeEnd":808,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/plugins/mail/impl/src/main/java/org/pentaho/di/trans/steps/mail/Mail.java#L772-L808","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nauthenticationToken = \"${OAUTH_REFRESH}\"; // expired refresh token\n// after\nauthenticationToken = \"1//0abc...\"; // freshly generated refresh token, redirect_uri matches app registration","handlingStrategy":"try-catch","validationCode":"// Pre-flight: verify OAuth config fields are non-empty before sending\nif (Utils.isEmpty(oauthUsername) || Utils.isEmpty(authenticationToken)) {\n  throw new KettleException(\"OAuth settings incomplete: username and refresh token are required.\");\n}","typeGuard":null,"tryCatchPattern":"try { sendMail(...); } catch (RuntimeException e) {\n  Throwable c = e.getCause();\n  logError(\"OAuth token acquisition failed (check client id/secret, refresh token, redirect URI): \" + (c != null ? c.getMessage() : e.getMessage()), e);\n}","preventionTips":["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."],"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"}