pentaho/pentaho-kettle · error · KettleException

JobJobError.Recursive

Error message

JobJobError.Recursive

What it means

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.

Solutions

  1. Point the Job entry's filename to a different sub-job .kjb file
  2. Fix the variable so it does not resolve to the parent job's own path
  3. If recursion is intended, restructure with distinct jobs or use a different execution mechanism
  4. Check for path aliases (symlinks, VFS mounts) that make paths compare equal

Example fix

// before
//   Filename: ${Internal.Job.Filename}   (points at the job itself)
// after
//   Filename: ${Internal.Job.Filename.Directory}/child_job.kjb
Defensive patterns

Strategy: validation

Validate before calling

String parentFile = parentJobMeta.getFilename();
String subFile = environmentSubstitute(jobEntry.getFilename());
if (parentFile != null && parentFile.equals(subFile)) {
  throw new IllegalArgumentException("Job entry references its own job file: " + subFile);
}

Try / catch

try {
  jobEntry.execute(result, nr);
} catch (KettleException e) {
  if (e.getMessage().contains("recursive") || e.getMessage().contains("Recursive")) {
    // fix the entry configuration; retrying cannot succeed
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/836d7292ddb4e197. Report an issue: GitHub.

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/job/entries/job/JobEntryJob.java:1344

  private void verifyRecursiveExecution( Job parentJob, JobMeta jobMeta ) throws KettleException {

    if ( parentJob == null ) {
      return; // OK!
    }

    JobMeta parentJobMeta = parentJob.getJobMeta();

    if ( parentJobMeta.getName() == null && jobMeta.getName() != null ) {
      return; // OK
    }
    if ( parentJobMeta.getName() != null && jobMeta.getName() == null ) {
      return; // OK as well.
    }

    // Not from the repository? just verify the filename
    //
    if ( jobMeta.getFilename() != null && jobMeta.getFilename().equals( parentJobMeta.getFilename() ) ) {
      throw new KettleException( BaseMessages.getString( PKG, "JobJobError.Recursive", jobMeta.getFilename() ) );
    }

    //If file has VFS reference, just verify the filename
    if ( isVfsReference( jobMeta.getFilename() ) && !jobMeta.getFilename().equals( parentJobMeta.getFilename() ) ) {
      return;
    }

    // Different directories: OK
    if ( parentJobMeta.getRepositoryDirectory() == null && jobMeta.getRepositoryDirectory() != null ) {
      return;
    }
    if ( parentJobMeta.getRepositoryDirectory() != null && jobMeta.getRepositoryDirectory() == null ) {
      return;
    }
    if ( jobMeta.getRepositoryDirectory().getObjectId() != parentJobMeta.getRepositoryDirectory().getObjectId() ) {
      return;
    }

View on GitHub (pinned to f3058517a1)