pentaho/pentaho-kettle · error · RuntimeException

infinite loop detected: + key

Error message

infinite loop detected: + key

What it means

ClosureGenerator.recurseParents walks the parent map building the closure table. As a safeguard against cyclic parent-child data, it throws a RuntimeException when recursion depth exceeds 50. It means the hierarchy data contains a cycle (a row's ancestry loops back on itself) rather than forming a tree.

Solutions

  1. Find and fix the cycle in the source data (detect rows whose parent chain loops)
  2. Filter or flag self-referencing rows before the step
  3. Pre-validate the hierarchy in a prior step or with a SQL cycle check
  4. If legitimately deeper than 50 levels, adjust the hardcoded depth guard in a patched build

Example fix

// before (bad data)
row: id=A, parent=B; id=B, parent=A
// after
row: id=B, parent=null (break the cycle at the root)
Defensive patterns

Strategy: validation

Validate before calling

-- SQL cycle pre-check before the step
WITH RECURSIVE chain(id, parent, depth) AS (
  SELECT id, parent_id, 0 FROM tree
  UNION ALL
  SELECT t.id, t.parent_id, c.depth+1 FROM tree t JOIN chain c ON t.parent_id = c.id
  WHERE depth < 60
)
SELECT id FROM chain WHERE depth > 50;

Type guard

boolean isAcyclic(Map<Object,Object> parentMap) {
  for (Object k : parentMap.keySet()) {
    int d = 0; Object cur = k;
    while (cur != null && d <= 50) { cur = parentMap.get(cur); d++; }
    if (d > 50) return false;
  }
  return true;
}

Try / catch

try {
  step.processRow();
} catch (RuntimeException e) {
  if (e.getMessage().startsWith("infinite loop detected")) {
    // cycle in hierarchy data; identify the key from the message and fix source rows
  }
}

Prevention

When it happens

Trigger: Processing input rows where a record is its own ancestor — e.g. rows A->B, B->C, C->A — causing the recursive parent lookup to never terminate.

Common situations: Dirty hierarchical data (employee-manager cycles), self-referencing rows (A->A), importing hierarchies from systems without referential constraints.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at plugins/core/impl/src/main/java/org/pentaho/di/trans/steps/closure/ClosureGenerator.java:116

          Object[] outputRow = RowDataUtil.allocateRowData( data.outputRowMeta.size() );
          outputRow[ 0 ] = parent;
          outputRow[ 1 ] = current;
          outputRow[ 2 ] = data.parents.get( parent );
          putRow( data.outputRowMeta, outputRow );
        }
      }

      setOutputDone();
      return false;
    }

    return true;
  }

  private void recurseParents( Object key, long distance ) {
    // catch infinite loop - change at will
    if ( distance > 50 ) {
      throw new RuntimeException( "infinite loop detected:" + key );
    }
    Object parent = data.map.get( key );

    if ( parent == null || parent == data.topLevel || parent.equals( data.topLevel ) ) {
      return;
    } else {
      data.parents.put( parent, distance );
      recurseParents( parent, distance + 1 );
      return;
    }
  }

  public boolean init( StepMetaInterface smi, StepDataInterface sdi ) {
    meta = (ClosureGeneratorMeta) smi;
    data = (ClosureGeneratorData) sdi;

    if ( super.init( smi, sdi ) ) {
      data.reading = true;

View on GitHub (pinned to f3058517a1)