pentaho/pentaho-kettle · error · KettleXMLException

MemoryGroupByMeta.Exception.UnableToLoadStepInfoFromXML

MemoryGroupByMeta.Exception.UnableToLoadStepInfoFromXML

Error message

MemoryGroupByMeta.Exception.UnableToLoadStepInfoFromXML

What it means

MemoryGroupByMeta.readData() (invoked via loadXML) parses the MemoryGroupBy step's definition from a transformation XML node — grouping fields, aggregation fields/types, and the give_back_row flag. Any exception during this parsing is wrapped in a KettleXMLException with this message key, meaning the step could not be reconstructed from the XML. The original parse/conversion error is chained as the cause.

Solutions

  1. Inspect the chained cause to find the exact XML parsing failure (element name, line, value).
  2. Open the .ktr in a text editor and validate the MemoryGroupBy step's XML block; fix or remove malformed attributes/elements.
  3. Re-create the MemoryGroupBy step in Spoon and re-save the transformation to regenerate clean XML.
  4. Check the version of Pentaho used to create the file versus the runtime version; migrate the transformation if schemas differ.

Example fix

// before: ambiguous int parse on possibly-missing node
int nrValues = Integer.parseInt(XMLHandler.getTagValue(stepnode, "number_of_values"));

// after: default when absent
String nrStr = XMLHandler.getTagValue(stepnode, "number_of_values");
int nrValues = Utils.isEmpty(nrStr) ? 0 : Integer.parseInt(nrStr);
Defensive patterns

Strategy: try-catch

Validate before calling

// validate XML before loadXML
String xml = new String(Files.readAllBytes(Paths.get(ktrPath)), StandardCharsets.UTF_8);
Document doc = XMLHandler.loadXMLString(xml); // throws early on malformed XML
Node memGroup = XMLHandler.getSubNode(doc.getDocumentElement(), "step");
if (memGroup == null) throw new IllegalArgumentException("No step node for MemoryGroupBy in XML");

Type guard

boolean isParseableStepNode(Node stepnode) {
  return stepnode != null && XMLHandler.getSubNode(stepnode, "fields") != null;
}

Try / catch

try {
  meta.loadXML(stepnode, databases, metaStore);
} catch (KettleXMLException e) {
  logError("MemoryGroupBy XML load failed: " + e.getCause(), e);
  throw e;
}

Prevention

When it happens

Trigger: Loading a .ktr whose <step> node for MemoryGroupBy contains malformed or unexpected XML — missing required child elements, non-numeric values where ints are expected (e.g. number_of_values), or a structurally corrupt XML file.

Common situations: Hand-edited or truncated .ktr files; transformations produced by newer/older Pentaho versions with changed XML schema; version-control merge conflicts inside the XML; XML with wrong encoding that breaks parsing.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/steps/memgroupby/MemoryGroupByMeta.java:286

        subjectField[i] = XMLHandler.getTagValue( fnode, "subject" );
        aggregateType[i] = getType( XMLHandler.getTagValue( fnode, "type" ) );

        if ( aggregateType[i] == TYPE_GROUP_COUNT_ALL
          || aggregateType[i] == TYPE_GROUP_COUNT_DISTINCT || aggregateType[i] == TYPE_GROUP_COUNT_ANY ) {
          hasNumberOfValues = true;
        }

        valueField[i] = XMLHandler.getTagValue( fnode, "valuefield" );
      }

      String giveBackRow = XMLHandler.getTagValue( stepnode, "give_back_row" );
      if ( Utils.isEmpty( giveBackRow ) ) {
        alwaysGivingBackOneRow = hasNumberOfValues;
      } else {
        alwaysGivingBackOneRow = "Y".equalsIgnoreCase( giveBackRow );
      }
    } catch ( Exception e ) {
      throw new KettleXMLException( BaseMessages.getString(
        PKG, "MemoryGroupByMeta.Exception.UnableToLoadStepInfoFromXML" ), e );
    }
  }

  public static final int getType( String desc ) {
    for ( int i = 0; i < typeGroupCode.length; i++ ) {
      if ( typeGroupCode[i].equalsIgnoreCase( desc ) ) {
        return i;
      }
    }
    for ( int i = 0; i < typeGroupLongDesc.length; i++ ) {
      if ( typeGroupLongDesc[i].equalsIgnoreCase( desc ) ) {
        return i;
      }
    }
    return 0;
  }

View on GitHub (pinned to f3058517a1)