pentaho/pentaho-kettle · error · KettleDatabaseException
Failed to fetch driver metadata for
Error message
Failed to fetch driver metadata for '{driverId}' from '{metadataUrl}': {message} What it means
Catch-all failure while fetching or parsing driver metadata: any Exception that is not a KettleDatabaseException during the HTTP request or JSON deserialization (new ObjectMapper().readValue) is rethrown as this error, preserving the cause message. This covers connection failures, malformed URLs, IO errors, and non-JSON bodies.
Solutions
- Read the cause message in the exception — it names the underlying IO/parse problem
- Test the exact metadataUrl (printed in the error) with curl to see what the service actually returns
- Fix JDBC_DRIVER_SERVICE_URL scheme/host/port if the URL itself is malformed or the host is unreachable
- Check network/firewall/proxy rules between PDI and the metadata service
- If the body is invalid JSON, fix or upgrade the metadata service
Example fix
// before export JDBC_DRIVER_SERVICE_URL=htp://metadata:8080 // bad scheme -> MalformedURLException // after export JDBC_DRIVER_SERVICE_URL=http://metadata:8080
Defensive patterns
Strategy: try-catch
Validate before calling
try { new URL(baseUrl).toURI(); } catch (URISyntaxException e) { throw new IllegalStateException("JDBC_DRIVER_SERVICE_URL is malformed: " + baseUrl, e); }
InetAddress.getByName(host); // fail fast on DNS Try / catch
try { map = db.driverMetadata(driverId); } catch (KettleDatabaseException e) { if (e.getMessage().startsWith("Failed to fetch driver metadata")) { Throwable c = e.getCause(); if (c instanceof java.io.IOException) { log.warn("Network/IO problem: " + c.getMessage()); retryWithBackoff(); } else if (c instanceof com.fasterxml.jackson.core.JsonProcessingException) { log.error("Service returned non-JSON body"); } else throw e; } else throw e; } Prevention
- Validate the service URL scheme/host at startup
- Add timeouts on the metadata service side so connections don't hang
- Ensure the service always returns JSON with content-type application/json
- Check DNS/proxy/TLS configuration between PDI and the service
When it happens
Trigger: driverMetadata -> getMetadataFromDriver: malformed JDBC_DRIVER_SERVICE_URL (MalformedURLException), host unreachable (ConnectException/UnknownHostException), connection reset/timeout, or the response body is not valid JSON / doesn't fit a Map.
Common situations: Metadata service hostname wrong or DNS broken; service returns an HTML error page instead of JSON; TLS handshake failure; typo in service URL scheme (htp://); response interrupted by proxy.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
- Failed to fetch driver metadata for
- Cannot fetch driver metadata for
- Carte.Error.NoServerFound
- Client error creating folder
- CmsTokenProvider: Keycloak token request failed — HTTP
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/3e8c4db39c4a4608.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/pentaho/di/core/database/Database.java:762
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();
}
}
}
/**
* Disconnect from the database and close all open prepared statements.
*/
public synchronized void disconnect() {
try {
if ( connection == null || connection.isClosed() ) {
closeDynamicClassLoader();
return; // Nothing to do...
}
} catch ( SQLException ex ) {View on GitHub (pinned to f3058517a1)