{"record":{"id":"836d7292ddb4e197","repo":"pentaho/pentaho-kettle","slug":"jobjoberror-recursive","errorCode":null,"errorMessage":"JobJobError.Recursive","messagePattern":"JobJobError\\.Recursive","errorType":"exception","errorClass":"KettleException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/job/entries/job/JobEntryJob.java","lineNumber":1344,"sourceCode":"  private void verifyRecursiveExecution( Job parentJob, JobMeta jobMeta ) throws KettleException {\n\n    if ( parentJob == null ) {\n      return; // OK!\n    }\n\n    JobMeta parentJobMeta = parentJob.getJobMeta();\n\n    if ( parentJobMeta.getName() == null && jobMeta.getName() != null ) {\n      return; // OK\n    }\n    if ( parentJobMeta.getName() != null && jobMeta.getName() == null ) {\n      return; // OK as well.\n    }\n\n    // Not from the repository? just verify the filename\n    //\n    if ( jobMeta.getFilename() != null && jobMeta.getFilename().equals( parentJobMeta.getFilename() ) ) {\n      throw new KettleException( BaseMessages.getString( PKG, \"JobJobError.Recursive\", jobMeta.getFilename() ) );\n    }\n\n    //If file has VFS reference, just verify the filename\n    if ( isVfsReference( jobMeta.getFilename() ) && !jobMeta.getFilename().equals( parentJobMeta.getFilename() ) ) {\n      return;\n    }\n\n    // Different directories: OK\n    if ( parentJobMeta.getRepositoryDirectory() == null && jobMeta.getRepositoryDirectory() != null ) {\n      return;\n    }\n    if ( parentJobMeta.getRepositoryDirectory() != null && jobMeta.getRepositoryDirectory() == null ) {\n      return;\n    }\n    if ( jobMeta.getRepositoryDirectory().getObjectId() != parentJobMeta.getRepositoryDirectory().getObjectId() ) {\n      return;\n    }\n","sourceCodeStart":1326,"sourceCodeEnd":1362,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/job/entries/job/JobEntryJob.java#L1326-L1362","documentation":"verifyRecursiveExecution() prevents a job entry from launching the same job it is already part of (infinite recursion). When the sub-job was loaded from a file and its filename equals the parent job's filename, a KettleException with localized message 'JobJobError.Recursive' (containing the filename) is thrown from the JobEntryJob constructor path.","triggerScenarios":"Configuring a Job entry whose filename points to the same .kjb file as the parent job (e.g. selecting the job's own file, or a variable resolving to the parent's path), then executing/validating the entry.","commonSituations":"Variable like ${Internal.Job.Filename} accidentally used as the sub-job filename; copy-paste of a job that calls itself; symlink/path alias making two different strings normalize to the same file.","solutions":["Point the Job entry's filename to a different sub-job .kjb file","Fix the variable so it does not resolve to the parent job's own path","If recursion is intended, restructure with distinct jobs or use a different execution mechanism","Check for path aliases (symlinks, VFS mounts) that make paths compare equal"],"exampleFix":"// before\n//   Filename: ${Internal.Job.Filename}   (points at the job itself)\n// after\n//   Filename: ${Internal.Job.Filename.Directory}/child_job.kjb","handlingStrategy":"validation","validationCode":"String parentFile = parentJobMeta.getFilename();\nString subFile = environmentSubstitute(jobEntry.getFilename());\nif (parentFile != null && parentFile.equals(subFile)) {\n  throw new IllegalArgumentException(\"Job entry references its own job file: \" + subFile);\n}","typeGuard":null,"tryCatchPattern":"try {\n  jobEntry.execute(result, nr);\n} catch (KettleException e) {\n  if (e.getMessage().contains(\"recursive\") || e.getMessage().contains(\"Recursive\")) {\n    // fix the entry configuration; retrying cannot succeed\n  }\n  throw e;\n}","preventionTips":["Never point a Job entry at the parent job's own .kjb file","Be careful with ${Internal.Job.Filename} variables in sub-job fields","Check for symlinks/path aliases that make two paths compare equal","Restructure intentionally recursive workflows into distinct, parameterized jobs"],"tags":["pdi","kettle","recursion","configuration"],"backgroundTag":"invalid-argument-value","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"}