pentaho/pentaho-kettle · error · KettleException
JobJob.Exception.ReferencedTransformationIdIsNull
Error message
JobJob.Exception.ReferencedTransformationIdIsNull
What it means
Same validation branch as the previous error: when specificationMethod is REPOSITORY_BY_REFERENCE and metaObjectId is null, getMetaForEntry throws a KettleException whose message key is chosen by entry type. This index corresponds to the else-branch message 'JobJob.Exception.ReferencedTransformationIdIsNull', used when the entry wraps a nested JOB (not a transformation). The meaning is identical: by-reference mode requires a non-null repository object ID.
Solutions
- Resolve and set the nested job's ObjectId: entry.setObjectId(rep.getJobId(name, directory)) (or the ObjectId from rep.getObjectInformation) before execution.
- Alternatively reference the nested job by name (REPOSITORY_BY_NAME with directory/name set) or by file path (FILENAME).
- Re-open and re-save the job in Spoon, re-selecting the referenced job to restore the stored object ID.
- Audit migrated repositories for by-reference entries with missing IDs and remap them to the target repository's IDs.
Example fix
// before jobEntryJob.setSpecificationMethod( SPECIFICATION_METHOD.REPOSITORY_BY_REFERENCE ); // metaObjectId == null -> 'ReferencedTransformationIdIsNull' // after ObjectId id = rep.getJobId( "NestedJob", "/production" ); jobEntryJob.setSpecificationMethod( SPECIFICATION_METHOD.REPOSITORY_BY_REFERENCE ); jobEntryJob.setObjectId( id );
Defensive patterns
Strategy: validation
Validate before calling
// validate nested-job entries before running the parent job
for ( JobEntryCopy c : jobMeta.getJobCopies() ) {
JobEntryInterface e = c.getEntry();
if ( e.getSpecificationMethod() == SPECIFICATION_METHOD.REPOSITORY_BY_REFERENCE
&& e.getObjectId() == null ) {
throw new IllegalStateException( "Nested job entry '" + e.getName() + "' has a null repository object ID" );
}
} Type guard
boolean hasResolvableJobReference( JobEntryInterface e ) {
switch ( e.getSpecificationMethod() ) {
case FILENAME: return e.getFilename() != null;
case REPOSITORY_BY_NAME: return e.getDirectoryName() != null && e.getName() != null;
case REPOSITORY_BY_REFERENCE: return e.getObjectId() != null;
default: return false;
}
} Try / catch
try {
meta = loader.getMetaForEntry( bowl, rep, metaStore, space );
} catch ( KettleException ke ) {
logError( "Nested job reference unresolved: " + ke.getMessage(), ke );
throw ke; // KettleException is rethrown as-is by the loader
} Prevention
- Resolve nested job IDs via rep.getJobId(name, directory) at job-build time, not at run time.
- Keep design-time and runtime PDI versions aligned to avoid losing reference metadata in exports.
- Validate every job in CI by loading it and checking all entries have resolvable references.
- Avoid hand-editing kjb files; edit through Spoon or the metadata APIs.
When it happens
Trigger: getMetaForEntry invoked for a JobEntryJob (isTransMeta() == false) configured with specificationMethod = REPOSITORY_BY_REFERENCE while metaObjectId is null — e.g. the nested-job entry was never linked to an actual repository job object.
Common situations: A 'Job' job entry pointing at a repository job by reference whose ID was dropped during copy/paste, export/import between repositories, or programmatic job construction; hand-edited kjb files missing the reference id; CI/CD pipelines that assemble jobs dynamically without resolving the target object's ObjectId.
Related errors
- JobTrans.Exception.ReferencedTransformationIdIsNull
- Could not execute specified in a repository since we're not…
- ERROR_0002_Cannot_Load_Job_From_Repository
- JobEntryCheckFilesLocked.UnableToLoadFromRepo
- JobEntryCheckFilesLocked.UnableToSaveToRepo
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/a9d70857811e5fc9.
Report an issue: GitHub.
Appendix: source
Thrown at engine/src/main/java/org/pentaho/di/base/MetaFileLoaderImpl.java:247
? (T) new TransMeta( bowl, metaPath, metaStore, null, true, jobEntryBase.getParentVariableSpace(),
null )
: getMetaFromRepository( bowl, rep, r, metaPath, tmpSpace );
} else {
theMeta = getMetaFromRepository( bowl, rep, r, metaPath, tmpSpace );
}
if ( theMeta != null ) {
idContainer[ 0 ] = metaPath;
}
}
}
break;
case REPOSITORY_BY_REFERENCE:
if ( metaObjectId == null ) {
if ( isTransMeta() ) {
throw new KettleException( BaseMessages.getString( persistentClass,
"JobTrans.Exception.ReferencedTransformationIdIsNull" ) );
} else {
throw new KettleException( BaseMessages.getString( persistentClass,
"JobJob.Exception.ReferencedTransformationIdIsNull" ) );
}
}
if ( rep != null ) {
theMeta = attemptCacheRead( metaObjectId.toString() ); //try to get from the cache first
if ( theMeta == null ) {
// Load the last revision
if ( isTransMeta() ) {
theMeta = (T) rep.loadTransformation( metaObjectId, null );
} else {
theMeta = (T) rep.loadJob( metaObjectId, null );
}
idContainer[ 0 ] = metaObjectId.toString();
}
} else {
throw new KettleException(
"Could not execute " + friendlyMetaType + " specified in a repository since we're not connected to one" );View on GitHub (pinned to f3058517a1)