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
- Inspect the chained cause to find which status field/sample row fails to serialize
- Check step sample rows for characters that break XML encoding (invalid control chars) and sanitize input data
- Update Pentaho/Kettle — older versions had XML-encoding edge cases in status servlets
- 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
- Sanitize/reject input rows containing control characters
- Keep Kettle updated for XML-encoding fixes in the status servlet
- Limit sample size in monitoring configuration
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
- AddSequenceMeta.Exception.ErrorLoadingStepInfo
- AnalyticQueryMeta.Exception.UnableToLoadStepInfoFromXML
- ChangeFileEncodingMeta.Exception.UnableToReadStepInfo
- CombinationLookupMeta.Exception.UnableToLoadStepInfo
- Error loading transformation step from XML
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)