pentaho/pentaho-kettle · error · KettleDatabaseException

Failed to fetch driver metadata for

Error message

Failed to fetch driver metadata for '{driverId}' — HTTP {status} from {metadataUrl}

What it means

The driver-metadata HTTP service responded with a non-200 status. getMetadataFromDriver treats anything other than HTTP 200 as a hard failure and includes the driverId, status code, and requested URL in the message. It wraps problems like 404 (unknown driver), 401/403 (bad bearer token), and 5xx (service down).

Solutions

  1. Check the status code in the message: 404 => fix driverId or deploy the driver definition; 401/403 => fix the bearer token; 5xx => check service health
  2. Verify JDBC_DRIVER_SERVICE_URL points at the correct service root (no missing path prefix, no trailing duplication)
  3. Confirm the driverId exists by calling the same REST endpoint manually (curl) with the same auth header
  4. Retry if the status is 503/transient and the service is restarting

Example fix

// before
Database.driverMetadata("mysql8x"); // 404: driver not published
// after
Database.driverMetadata("MySQL"); // id that the service actually exposes
// or fix token:
conn.setRequestProperty("Authorization", "Bearer " + validToken);
Defensive patterns

Strategy: try-catch

Validate before calling

HttpURLConnection probe = (HttpURLConnection) new URL(baseUrl + "/api/v1/connection-drivers/health").openConnection();
if (probe.getResponseCode() != 200) throw new IllegalStateException("Driver metadata service unhealthy: " + probe.getResponseCode());

Try / catch

try { map = db.driverMetadata(driverId); } catch (KettleDatabaseException e) { if (e.getMessage().matches(".*HTTP \\d+.*")) { int status = parseStatus(e.getMessage()); if (status >= 500 || status == 429) retryWithBackoff(); else log.error("Permanent failure for driverId, status " + status); } else throw e; }

Prevention

When it happens

Trigger: driverMetadata -> getMetadataFromDriver issues GET {base}/api/v1/connection-drivers/{driverId} and the server returns e.g. 404, 401, 403, 500, or 503 instead of 200.

Common situations: Typo'd driverId; metadata service does not know the driver version; expired or missing bearer token (Authorization header rejected); metadata service partially deployed and returning 5xx; a proxy stripping the path.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

Thrown at core/src/main/java/org/pentaho/di/core/database/Database.java:752

          + "Set the environment variable or system property 'JDBC_DRIVER_SERVICE_URL'." );
    }

    String metadataUrl = serviceBaseUrl + "/api/v1/connection-drivers/" + driverId;
    HttpURLConnection conn = null;
    try {
      conn = (HttpURLConnection) new URL( metadataUrl ).openConnection();
      conn.setConnectTimeout( 10_000 );
      conn.setReadTimeout( 60_000 );
      conn.setRequestMethod( "GET" );

      String bearerToken = CmsTokenProvider.getInstance().getToken();
      if ( bearerToken != null ) {
        conn.setRequestProperty( "Authorization", "Bearer " + bearerToken );
      }

      int status = conn.getResponseCode();
      if ( status != HttpURLConnection.HTTP_OK ) {
        throw new KettleDatabaseException(
          "Failed to fetch driver metadata for '" + driverId + "' — HTTP " + status + " from " + metadataUrl );
      }

      try ( java.io.InputStream is = conn.getInputStream() ) {
        return new ObjectMapper().readValue( is, Map.class );
      }
    } catch ( KettleDatabaseException e ) {
      throw e;
    } catch ( Exception e ) {
      throw new KettleDatabaseException(
        "Failed to fetch driver metadata for '" + driverId + "' from '" + metadataUrl + "': " + e.getMessage(), e );
    } finally {
      if ( conn != null ) {
        conn.disconnect();
      }
    }
  }

View on GitHub (pinned to f3058517a1)