pentaho/pentaho-kettle · error · RuntimeException

Error calling extension point at end of transformation

Error message

Error calling extension point at end of transformation

What it means

Pentaho Kettle (PDI) fires the TransformationFinish extension point when a transformation completes. If any registered extension point plugin throws a KettleException during that callback, Trans wraps it in a RuntimeException with this message. The transformation itself has finished; the failure is in a plugin hooking the end-of-transformation lifecycle.

Solutions

  1. Check the cause stack trace to identify which extension point plugin threw
  2. Fix or update the failing plugin (check its DB/file/network access at transformation end)
  3. Temporarily remove the plugin jar from the plugins directory to confirm it is the culprit
  4. Wrap the plugin's callExtensionPoint body with its own error handling/logging so it does not propagate

Example fix

// before: plugin propagates errors
public void callExtensionPoint(LogChannelInterface log, Object object) throws KettleException {
  writeAudit((Trans) object);
}
// after: plugin handles its own failures
public void callExtensionPoint(LogChannelInterface log, Object object) throws KettleException {
  try { writeAudit((Trans) object); }
  catch (Exception e) { log.logError("Audit write failed", e); }
}
Defensive patterns

Strategy: try-catch

Validate before calling

List<String> plugins = KettleExtensionPoint.TransformationFinish.getExtensions();
if (plugins != null && !plugins.isEmpty()) System.out.println("Finish plugins registered: " + plugins);

Type guard

boolean isExtensionPointFailure(RuntimeException e) {
  return "Error calling extension point at end of transformation".equals(e.getMessage()) && e.getCause() instanceof KettleException;
}

Try / catch

try {
  trans.execute(args); trans.waitUntilFinished();
} catch (RuntimeException e) {
  if (e.getCause() instanceof KettleException) {
    log.warn("Transformation finished but an extension point plugin failed", e.getCause());
  } else throw e;
}

Prevention

When it happens

Trigger: A plugin registered under the KettleExtensionPoint.TransformationFinish extension point throws a KettleException inside its callExtensionPoint() implementation while Trans.executeTransformation()/run() is finishing.

Common situations: A custom or third-party plugin (e.g. a monitoring/audit listener) tries to write to a database, file, or remote endpoint at transformation end and fails due to connectivity, permissions, or a bug in the plugin; an outdated plugin incompatible with the current Kettle version.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/Trans.java:1501

    transFinishedBlockingQueue = new ArrayBlockingQueue<>( TRANS_FINISHED_BLOCKING_QUEUE_SIZE );

    TransListener transListener = new TransAdapter() {
      @Override
      public void transFinished( Trans trans ) {

        try {
          shutdownHeartbeat( trans != null ? trans.heartbeat : null );
          if ( trans != null && transMeta.getParent() == null && trans.parentJob == null && trans.parentTrans == null ) {
            if ( log.isDetailed() && transMeta.getMetaFileCache() != null ) {
              transMeta.getMetaFileCache().logCacheSummary( log );
            }
            transMeta.setMetaFileCache( null );
          }

          ExtensionPointHandler.callExtensionPoint( log, KettleExtensionPoint.TransformationFinish.id, trans );
        } catch ( KettleException e ) {
          throw new RuntimeException( "Error calling extension point at end of transformation", e );
        }

        // First of all, stop the performance snapshot timer if there is is
        // one...
        //
        if ( transMeta.isCapturingStepPerformanceSnapShots() && stepPerformanceSnapShotTimer != null ) {
          stepPerformanceSnapShotTimer.cancel();
        }

        transMeta.disposeEmbeddedMetastoreProvider();

        setFinished( true );
        setRunning( false ); // no longer running

        log.snap( Metrics.METRIC_TRANSFORMATION_EXECUTION_STOP );

        // If the user ran with metrics gathering enabled and a metrics logging table is configured, add another
        // listener...

View on GitHub (pinned to f3058517a1)