pentaho/pentaho-kettle · error · KettleException

Rest.Error.CanNotReadURL

Rest.Error.CanNotReadURL

Error message

Rest.Error.CanNotReadURL

What it means

Rest.callRest wraps the whole REST invocation (client creation, request build, execution) and converts any exception into a KettleException with the localized message Rest.Error.CanNotReadURL, including the resolved URL (data.realUrl). It is the generic 'the REST call did not succeed' boundary for this Pentaho Kettle step.

Solutions

  1. Read the chained cause exception (getCause()) to find the real failure (UnknownHostException, ConnectException, SSLHandshakeException, etc.)
  2. Verify the URL configured in the step (or in the URL input field for the current row) is correct and reachable (curl it)
  3. Check hostname resolution and firewall/proxy settings on the machine running the transformation
  4. If the URL comes from a field, preview the row data to confirm the value is a valid URL
  5. Test SSL configuration (trust store, 'ignore SSL' option) if the cause is a handshake failure

Example fix

// before
data.realUrl = urlInField ? row[urlFieldIdx] : meta.getUrl();
// after
String candidate = urlInField ? row[urlFieldIdx] : meta.getUrl();
if ( candidate == null || !candidate.matches( "^https?://.+" ) ) {
  throw new KettleException( "Invalid REST URL: " + candidate );
}
data.realUrl = candidate;
Defensive patterns

Strategy: try-catch

Validate before calling

String url = meta.isUrlInField() ? (String) rowData[data.indexOfUrlField] : environmentSubstitute( meta.getUrl() );
if ( url == null || !url.matches( "^https?://[^\s]+$" ) ) {
  throw new KettleException( "Invalid REST URL: " + url );
}

Try / catch

try {
  Object[] out = step.callRest( row );
} catch ( KettleException e ) {
  Throwable cause = e.getCause();
  if ( cause instanceof java.net.UnknownHostException ) { /* fix DNS */ }
  else if ( cause instanceof javax.net.ssl.SSLException ) { /* fix trust store */ }
  else if ( cause instanceof java.net.ConnectException ) { /* service down */ }
  log.error( "REST call to " + realUrl + " failed", e );
}

Prevention

When it happens

Trigger: Any failure inside callRest: malformed URL, unknown host, connection refused, invalid method field, SSL errors, or a runtime failure while building/executing the request against data.realUrl.

Common situations: Typo or missing protocol in the URL field; DNS failure in an air-gapped environment; target service down; URL taken from an input field containing bad data at runtime.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at plugins/rest/core/src/main/java/org/pentaho/di/trans/steps/rest/Rest.java:83

 */
public class Rest extends BaseStep implements StepInterface {
  private static Class<?> PKG = RestMeta.class; // for i18n purposes, needed by Translator2!!

  private static final String HEADER_CONTENT_TYPE = "Content-Type";

  private RestMeta meta;
  private RestData data;

  public Rest( StepMeta stepMeta, StepDataInterface stepDataInterface, int copyNr, TransMeta transMeta, Trans trans ) {
    super( stepMeta, stepDataInterface, copyNr, transMeta, trans );
  }

  protected Object[] callRest( Object[] rowData ) throws KettleException {
    try ( Client client = getClient( rowData ) ) {
      WebTarget target = buildRequest( client, rowData );
      return invokeRequest( target, rowData );
    } catch ( Exception e ) {
      throw new KettleException( BaseMessages.getString( PKG, "Rest.Error.CanNotReadURL", data.realUrl ), e );
    }
  }

  protected Client getClient( Object[] rowData ) throws KettleException {
    // get dynamic url ?
    if ( meta.isUrlInField() ) {
      data.realUrl = data.inputRowMeta.getString( rowData, data.indexOfUrlField );
    }

    // get dynamic method?
    if ( meta.isDynamicMethod() ) {
      data.method = data.inputRowMeta.getString( rowData, data.indexOfMethod );
      if ( Utils.isEmpty( data.method ) ) {
        throw new KettleException( BaseMessages.getString( PKG, "Rest.Error.MethodMissing" ) );
      }
    }

    if ( isDetailed() ) {

View on GitHub (pinned to f3058517a1)