{"record":{"id":"a9d70857811e5fc9","repo":"pentaho/pentaho-kettle","slug":"jobjob-exception-referencedtransformationidisnull","errorCode":null,"errorMessage":"JobJob.Exception.ReferencedTransformationIdIsNull","messagePattern":"JobJob\\.Exception\\.ReferencedTransformationIdIsNull","errorType":"exception","errorClass":"KettleException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/base/MetaFileLoaderImpl.java","lineNumber":247,"sourceCode":"                  ? (T) new TransMeta( bowl, metaPath, metaStore, null, true, jobEntryBase.getParentVariableSpace(),\n                                       null )\n                  : getMetaFromRepository( bowl, rep, r, metaPath, tmpSpace );\n              } else {\n                theMeta = getMetaFromRepository( bowl, rep, r, metaPath, tmpSpace );\n              }\n              if ( theMeta != null ) {\n                idContainer[ 0 ] = metaPath;\n              }\n            }\n          }\n          break;\n        case REPOSITORY_BY_REFERENCE:\n          if ( metaObjectId == null ) {\n            if ( isTransMeta() ) {\n              throw new KettleException( BaseMessages.getString( persistentClass,\n                \"JobTrans.Exception.ReferencedTransformationIdIsNull\" ) );\n            } else {\n              throw new KettleException( BaseMessages.getString( persistentClass,\n                \"JobJob.Exception.ReferencedTransformationIdIsNull\" ) );\n            }\n          }\n\n          if ( rep != null ) {\n            theMeta = attemptCacheRead( metaObjectId.toString() ); //try to get from the cache first\n            if ( theMeta == null ) {\n              // Load the last revision\n              if ( isTransMeta() ) {\n                theMeta = (T) rep.loadTransformation( metaObjectId, null );\n              } else {\n                theMeta = (T) rep.loadJob( metaObjectId, null );\n              }\n              idContainer[ 0 ] = metaObjectId.toString();\n            }\n          } else {\n            throw new KettleException(\n              \"Could not execute \" + friendlyMetaType + \" specified in a repository since we're not connected to one\" );","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/base/MetaFileLoaderImpl.java#L229-L265","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\njobEntryJob.setSpecificationMethod( SPECIFICATION_METHOD.REPOSITORY_BY_REFERENCE );\n// metaObjectId == null -> 'ReferencedTransformationIdIsNull'\n\n// after\nObjectId id = rep.getJobId( \"NestedJob\", \"/production\" );\njobEntryJob.setSpecificationMethod( SPECIFICATION_METHOD.REPOSITORY_BY_REFERENCE );\njobEntryJob.setObjectId( id );","handlingStrategy":"validation","validationCode":"// validate nested-job entries before running the parent job\nfor ( JobEntryCopy c : jobMeta.getJobCopies() ) {\n  JobEntryInterface e = c.getEntry();\n  if ( e.getSpecificationMethod() == SPECIFICATION_METHOD.REPOSITORY_BY_REFERENCE\n       && e.getObjectId() == null ) {\n    throw new IllegalStateException( \"Nested job entry '\" + e.getName() + \"' has a null repository object ID\" );\n  }\n}","typeGuard":"boolean hasResolvableJobReference( JobEntryInterface e ) {\n  switch ( e.getSpecificationMethod() ) {\n    case FILENAME: return e.getFilename() != null;\n    case REPOSITORY_BY_NAME: return e.getDirectoryName() != null && e.getName() != null;\n    case REPOSITORY_BY_REFERENCE: return e.getObjectId() != null;\n    default: return false;\n  }\n}","tryCatchPattern":"try {\n  meta = loader.getMetaForEntry( bowl, rep, metaStore, space );\n} catch ( KettleException ke ) {\n  logError( \"Nested job reference unresolved: \" + ke.getMessage(), ke );\n  throw ke; // KettleException is rethrown as-is by the loader\n}","preventionTips":["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."],"tags":["kettle","job-entry","repository","null-reference","configuration"],"backgroundTag":"null-argument","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}