pentaho/pentaho-kettle · error · KettleException
The number of notes read [
Error message
The number of notes read [
What it means
After reading note-pad nodes from the stored job DataNode and adding them to jobMeta, JobDelegate verifies jobMeta.nrNotes() equals the expected nrNotes recorded when the job was saved. A mismatch means the note list in the repository is inconsistent with its recorded count, so loading is aborted to avoid silently returning a job with missing (or duplicated) notes.
Solutions
- Restore the job from a backup or re-save it from the PDI client to rewrite consistent note nodes.
- Inspect the job's notes node in the repository to find missing/duplicate note children.
- Align PDI client and Pentaho server versions to eliminate serialization differences.
- Delete and re-import the job from a known-good .kjb file.
- Catch KettleException in loading code and fall back to re-import or a cached copy of the job.
Example fix
// before
JobMeta jobMeta = (JobMeta) repository.loadJob(jobId, null).loadAll(); // throws on note-count mismatch
// after
try {
jobMeta = (JobMeta) repository.loadJob(jobId, null).loadAll();
} catch (KettleException e) {
if (e.getMessage().startsWith("The number of notes read")) {
jobMeta = importJobFromFile(backupKjbPath);
} else {
throw e;
}
} Defensive patterns
Strategy: try-catch
Try / catch
try {
JobMeta jobMeta = (JobMeta) repository.loadJob(jobId, null).loadAll();
} catch (KettleException e) {
if (e.getMessage().startsWith("The number of notes read")) {
// note list inconsistent -> re-import or restore job
} else {
throw e;
}
} Prevention
- Avoid aborted/partial saves; retry failed saves from a clean client state.
- Serialize job edits through one client or use repository locking.
- Keep versions aligned between PDI and the Pentaho server.
- Maintain known-good .kjb exports for restore.
When it happens
Trigger: Loading a job whose DataNode's NODE_NOTES children count differs from the persisted nrNotes value — typically after a corrupted or partial save, a concurrent writer, or a repository content-serialization version mismatch.
Common situations: Same as job-copy mismatch: failed saves, concurrent PDI clients editing the same job, cross-version import/export of repository content, manual tampering with repository storage.
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 [
- The number of job entry copies read [
- AbsSecurityProvider.ERROR_0002_UNABLE_TO_ACCESS_IS_ALLOWED
- AddSequenceMeta.Exception.UnableToReadStepInfo
- AddSequenceMeta.Exception.UnableToSaveStepInfo
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/44244bfea58c862d.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/pur/core/src/main/java/org/pentaho/di/repository/pur/JobDelegate.java:253
jobMeta.addJobEntry( copy );
}
if ( jobMeta.getJobCopies().size() != nrCopies ) {
throw new KettleException( "The number of job entry copies read [" + jobMeta.getJobCopies().size()
+ "] was not the number we expected [" + nrCopies + "]" );
}
// 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 );
jobMeta
.addNote( new NotePadMeta( XMLHandler.getSubNode( XMLHandler.loadXMLString( xml ), NotePadMeta.XML_TAG ) ) );
}
if ( jobMeta.nrNotes() != nrNotes ) {
throw new KettleException( "The number of notes read [" + jobMeta.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 copyFromName = getString( hopNode, JOB_HOP_FROM );
int copyFromNr = (int) hopNode.getProperty( JOB_HOP_FROM_NR ).getLong();
String copyToName = getString( hopNode, JOB_HOP_TO );
int copyToNr = (int) hopNode.getProperty( JOB_HOP_TO_NR ).getLong();
boolean enabled = true;
if ( hopNode.hasProperty( JOB_HOP_ENABLED ) ) {
enabled = hopNode.getProperty( JOB_HOP_ENABLED ).getBoolean();
}
View on GitHub (pinned to f3058517a1)