pentaho/pentaho-kettle · error · KettleException

Unable to serialize step

Error message

Unable to serialize step '{0}' status data to XML

What it means

KettleException thrown by StepStatus.getXML() when serializing a step's runtime status (metrics, samples) to XML fails. Any exception during XMLHandler tag building — e.g. from generating sample row data or number formatting — is wrapped in this message naming the failing step.

Solutions

  1. Inspect the chained cause to find which status field/sample row fails to serialize
  2. Check step sample rows for characters that break XML encoding (invalid control chars) and sanitize input data
  3. Update Pentaho/Kettle — older versions had XML-encoding edge cases in status servlets
  4. Retry status retrieval; if persistent, restart the transformation to reset corrupted step status state
Defensive patterns

Strategy: try-catch

Validate before calling

// Check sample rows for XML-hostile characters before building status
for (String sample : stepStatus.getSample()) {
  if (sample != null && sample.matches(".*[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F].*")) {
    log.warn("Sample contains control characters that may break XML serialization");
  }
}

Type guard

boolean isXmlSafe(String s) {
  return s == null || !s.matches(".*[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F].*");
}

Try / catch

try {
  String xml = stepStatus.getXML();
} catch (KettleException e) {
  log.error("Status serialization failed for step '" + stepName + "': " + e.getMessage(), e);
  // fall back to a minimal status without samples
  stepStatus.setSample(new String[0]);
  xml = stepStatus.getXML();
}

Prevention

When it happens

Trigger: getXML() on a StepStatus where building the XML fragment throws: parsing sample rows for XML output fails, I/O-style errors in XMLHandler.appendRepr, or null/invalid internal status fields producing exceptions during string building.

Common situations: Monitoring/monitoring-socket (trans status servlet) usage where a step's sample rows contain characters or data that break XML serialization; remote status polling of running transformations on a Carte server.

Understand the failure class

Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/step/StepStatus.java:157

        xml.append( sampleRowMeta.getMetaXML() );
        xml.append( Const.CR );
        if ( sampleRows != null ) {
          synchronized ( sampleRows ) {
            Iterator<Object[]> iterator = sampleRows.iterator();
            while ( iterator.hasNext() ) {
              Object[] sampleRow = iterator.next();
              xml.append( sampleRowMeta.getDataXML( sampleRow ) );
              xml.append( Const.CR );
            }
          }
        }
        xml.append( XMLHandler.closeTag( "samples" ) );
      }

      xml.append( XMLHandler.closeTag( XML_TAG ) );
      return xml.toString();
    } catch ( Exception e ) {
      throw new KettleException( "Unable to serialize step '" + stepname + "' status data to XML", e );
    }
  }

  public StepStatus( Node node ) throws KettleException {
    stepname = XMLHandler.getTagValue( node, "stepname" );
    copy = Integer.parseInt( XMLHandler.getTagValue( node, "copy" ) );
    linesRead = Long.parseLong( XMLHandler.getTagValue( node, "linesRead" ) );
    linesWritten = Long.parseLong( XMLHandler.getTagValue( node, "linesWritten" ) );
    linesInput = Long.parseLong( XMLHandler.getTagValue( node, "linesInput" ) );
    linesOutput = Long.parseLong( XMLHandler.getTagValue( node, "linesOutput" ) );
    linesUpdated = Long.parseLong( XMLHandler.getTagValue( node, "linesUpdated" ) );
    linesRejected = Long.parseLong( XMLHandler.getTagValue( node, "linesRejected" ) );
    errors = Long.parseLong( XMLHandler.getTagValue( node, "errors" ) );
    statusDescription = XMLHandler.getTagValue( node, "statusDescription" );
    seconds = Double.parseDouble( XMLHandler.getTagValue( node, "seconds" ) );
    speed = XMLHandler.getTagValue( node, "speed" );
    priority = XMLHandler.getTagValue( node, "priority" );
    stopped = "Y".equalsIgnoreCase( XMLHandler.getTagValue( node, "stopped" ) );

View on GitHub (pinned to f3058517a1)