pentaho/pentaho-kettle · error · KettleException
The number of notes read
Error message
The number of notes read [${notes}] was not the number we expected [${expected}] What it means
TransDelegate.dataNodeToElement rebuilds a TransMeta from a stored DataNode and counts the note (NotepadMeta) nodes it expects, then verifies transMeta.nrNotes() matches. If adding notes produced a different count than the stored node count, this KettleException is thrown, indicating the stored transformation data is inconsistent or notes were rejected during load.
Solutions
- Re-export/re-save the transformation from a working copy so its notes are re-serialized consistently.
- Inspect the stored NODE_NOTES data (PROP_XML of each note) for corrupt or duplicate entries.
- Compare the stored note count with the loaded count from the exception message to locate the discrepant note.
- Restore the transformation from a previous repository version.
Example fix
// before
DataNode noteNode : notesNode.getNodes() -> addNote( ... ); // count mismatch thrown
// after
for ( DataNode noteNode : notesNode.getNodes() ) {
String xml = getString( noteNode, PROP_XML );
if ( xml == null || xml.isEmpty() ) {
log.logBasic( "Skipping empty note XML in transformation " + transMeta.getName() );
continue; // and adjust expected nrNotes accordingly
}
transMeta.addNote( new NotePadMeta( XMLHandler.getSubNode( XMLHandler.loadXMLString( xml ), NotePadMeta.XML_TAG ) ) );
} Defensive patterns
Strategy: try-catch
Validate before calling
int storedNotes = notesNode.getNodes().size(); // after load: transMeta.nrNotes() must equal storedNotes
Try / catch
try {
delegate.dataNodeToElement( node, transMeta );
} catch ( KettleException e ) {
if ( e.getMessage().contains( "number of notes read" ) ) {
logError( "Corrupt transformation data; restoring previous version" );
restorePreviousVersion( transMeta.getObjectId() );
} else { throw e; }
} Prevention
- Don't manually edit repository backend data
- Restore transformations from version history when load integrity checks fail
- Keep Kettle versions consistent between writer and reader
- Re-save transformations after repository imports
When it happens
Trigger: Loading a transformation from the PUR repository where the number of note nodes under NODE_NOTES differs from the resulting TransMeta note count — e.g., duplicate/invalid note XML causes addNote to skip or the stored data was modified out-of-band.
Common situations: Manually edited repository content in the Jackrabbit backend, imports/export round-trips that lost or duplicated notes, corrupted transformation nodes, or version mismatches between writer and reader.
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
- The number of hops read
- TransLoadProgressDialog.Exception.ErrorLoadingTransformation
- TransSaveProgressDialog.Exception.ErrorSavingTransformation
- Unable to load transformation from path [" + absPath + "]
- Unable to load transformation with id [" + idTransformation…
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/086393db459d343f.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/pur/core/src/main/java/org/pentaho/di/repository/pur/TransDelegate.java:416
for ( int i = 0; i < transMeta.nrSteps(); i++ ) {
StepMeta stepMeta = transMeta.getStep( i );
StepMetaInterface sii = stepMeta.getStepMetaInterface();
if ( sii != null ) {
sii.searchInfoAndTargetSteps( transMeta.getSteps() );
}
}
// Read the notes...
//
DataNode notesNode = rootNode.getNode( NODE_NOTES );
int nrNotes = (int) notesNode.getProperty( PROP_NR_NOTES ).getLong();
for ( DataNode noteNode : notesNode.getNodes() ) {
String xml = getString( noteNode, PROP_XML );
transMeta
.addNote( new NotePadMeta( XMLHandler.getSubNode( XMLHandler.loadXMLString( xml ), NotePadMeta.XML_TAG ) ) );
}
if ( transMeta.nrNotes() != nrNotes ) {
throw new KettleException( "The number of notes read [" + transMeta.nrNotes()
+ "] was not the number we expected [" + nrNotes + "]" );
}
// Read the hops...
//
DataNode hopsNode = rootNode.getNode( NODE_HOPS );
int nrHops = (int) hopsNode.getProperty( PROP_NR_HOPS ).getLong();
for ( DataNode hopNode : hopsNode.getNodes() ) {
String stepFromName = getString( hopNode, TRANS_HOP_FROM );
String stepToName = getString( hopNode, TRANS_HOP_TO );
boolean enabled = true;
if ( hopNode.hasProperty( TRANS_HOP_ENABLED ) ) {
enabled = hopNode.getProperty( TRANS_HOP_ENABLED ).getBoolean();
}
StepMeta stepFrom = StepMeta.findStep( transMeta.getSteps(), stepFromName );
StepMeta stepTo = StepMeta.findStep( transMeta.getSteps(), stepToName );
View on GitHub (pinned to f3058517a1)