pentaho/pentaho-kettle · error · KettleException

Multiple input or output steps are not supported for a…

Error message

Multiple input or output steps are not supported for a single threaded mapping.

What it means

In Mapping.processRow's SingleThreaded branch, a single-threaded mapping (run in the SingleThreaded execution mode) requires at most one Mapping Input and one Mapping Output step. If mappingInputs.length > 1 or mappingOutputs.length > 1, a KettleException is thrown. Single-threaded execution streams everything through one thread and cannot arbitrate between multiple mapping entry/exit points.

Solutions

  1. Ensure the sub-transformation has exactly one Mapping Input and one Mapping Output step.
  2. Split the multi-input/multi-output sub-transformation into separate single-threaded mappings.
  3. Run with the standard execution engine instead of Single Threaded if multiple mapping inputs/outputs are required.
  4. Prefer the 'Simple Mapping (sub-transformation)' single-input/single-output style when targeting single-threaded mode.

Example fix

// before
new Trans(...).execute(null) with TransExecutionMode.SingleThreaded and sub.ktr having 2 MappingInputs
// after
// sub.ktr: keep 1 MappingInput + 1 MappingOutput, or execute with standard engine
Defensive patterns

Strategy: validation

Validate before calling

// Before enabling Single Threaded execution mode:
long in = subTransMeta.getSteps().stream().filter(s -> s.getStepMeta() instanceof MappingInputMeta).count();
long out = subTransMeta.getSteps().stream().filter(s -> s.getStepMeta() instanceof MappingOutputMeta).count();
if (in > 1 || out > 1) throw new IllegalStateException("SingleThreaded mapping supports max 1 input and 1 output (got " + in + "/" + out + ")");

Try / catch

try {
  trans.setTransExecutionMode(...); // SingleThreaded
  trans.execute(null);
} catch (KettleException e) {
  if (e.getMessage().contains("single threaded mapping")) {
    log.error("Mapping topology incompatible with SingleThreaded mode: use 1 input/1 output or standard engine", e);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Transformation executed with 'Single Threaded' execution mode (e.g. Trans.TransExecutionMode.SINGLE_THREAD) where the mapped sub-transformation declares more than one Mapping Input step or more than one Mapping Output step — checked in processRow's switch on mappingType == SingleThreaded.

Common situations: Reusing an ordinary mapping sub-transf with multiple inputs/outputs inside a single-threaded engine run (used for streaming/low-latency pipelines); switching execution mode to single-threaded without auditing the sub-transf topology.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/steps/mapping/Mapping.java:221

            // Set some statistics from the mapping...
            // This will show up in Spoon, etc.
            //
            Result result = getData().getMappingTrans().getResult();
            setErrors( result.getNrErrors() );
            setLinesRead( result.getNrLinesRead() );
            setLinesWritten( result.getNrLinesWritten() );
            setLinesInput( result.getNrLinesInput() );
            setLinesOutput( result.getNrLinesOutput() );
            setLinesUpdated( result.getNrLinesUpdated() );
            setLinesRejected( result.getNrLinesRejected() );
          }
          return false;

        case SingleThreaded:

          if ( mappingInputs.length > 1 || mappingOutputs.length > 1 ) {
            throw new KettleException(
                "Multiple input or output steps are not supported for a single threaded mapping." );
          }

          // Object[] row = getRow();
          // RowMetaInterface rowMeta = getInputRowMeta();

          // for (int count=0;count<(data.mappingTransMeta.getSizeRowset()/2) && row!=null;count++) {
          // // Pass each row over to the mapping input step, fill the buffer...
          //
          // mappingInputs[0].getInputRowSets().get(0).putRow(rowMeta, row);
          //
          // row = getRow();
          // }

          if ( ( log != null ) && log.isDebug() ) {
            List<RowSet> mappingInputRowSets = mappingInputs[0].getInputRowSets();
            log.logDebug( "# of input buffers: " + mappingInputRowSets.size() );
            if ( mappingInputRowSets.size() > 0 ) {

View on GitHub (pinned to f3058517a1)