pentaho/pentaho-kettle · error · KettleException

ProcessFiles.Error.TargetFileNotFile

Error message

ProcessFiles.Error.TargetFileNotFile

What it means

Thrown when the target path exists but is not a regular file (e.g. it is a folder). This guard prevents copy/move operations from overwriting an entire folder with a file, which would corrupt the destination directory.

Solutions

  1. Correct the target filename expression to include the file name, not just the folder (e.g. /out/dir/file.txt instead of /out/dir).
  2. Remove or rename the conflicting directory at the target path.
  3. Use step error handling to catch rows whose target collides with a directory and route them elsewhere.
  4. Compute target path as concat(targetFolder, "/", baseName(source)) so a folder-only value can never occur.

Example fix

// before
target = "/data/output/archive" // a directory
// after
target = "/data/output/archive/" concat basename(sourceFilename)
Defensive patterns

Strategy: validation

Validate before calling

// Guard: target must be a file path, and if it exists it must not be a directory
java.io.File t = new java.io.File(targetFilename);
boolean safe = !t.exists() || t.isFile();

Type guard

boolean safeTargetPath(String path) {
  java.io.File f = (path == null) ? null : new java.io.File(path);
  return f != null && (!f.exists() || f.isFile());
}

Try / catch

try {
  processRow();
} catch (KettleException e) {
  if (e.getMessage().contains("TargetFileNotFile")) {
    logError("Target points to a directory: " + extractPath(e));
  } else throw e;
}

Prevention

When it happens

Trigger: Operation Copy/Move; data.targetFile.exists() is true and data.targetFile.getType() != FileType.FILE.

Common situations: Target filename computed to a directory path by mistake (e.g. missing filename part in concatenation); user intended to move into a folder but gave the folder as the destination instead of folder + name; leftover directory at target location from previous runs.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/steps/processfiles/ProcessFiles.java:137

      }
      String targetFilename = null;
      if ( meta.getOperationType() != ProcessFilesMeta.OPERATION_TYPE_DELETE ) {
        // get value for target filename
        targetFilename = getInputRowMeta().getString( r, data.indexOfTargetFilename );

        if ( Utils.isEmpty( targetFilename ) ) {
          logError( BaseMessages.getString( PKG, "ProcessFiles.Error.TargetFileEmpty" ) );
          throw new KettleException( BaseMessages.getString( PKG, "ProcessFiles.Error.TargetFileEmpty" ) );
        }
        data.targetFile = KettleVFS.getInstance( getTransMeta().getBowl() ).getFileObject( targetFilename, getTransMeta() );
        if ( data.targetFile.exists() ) {
          if ( log.isDetailed() ) {
            logDetailed( BaseMessages.getString( PKG, "ProcessFiles.Log.TargetFileExists", targetFilename ) );
          }
          // check if target is really a file otherwise it could overwrite a complete folder by copy or move operations
          if ( data.targetFile.getType() != FileType.FILE ) {
            logError( BaseMessages.getString( PKG, "ProcessFiles.Error.TargetFileNotFile", targetFilename ) );
            throw new KettleException( BaseMessages.getString(
              PKG, "ProcessFiles.Error.TargetFileNotFile", targetFilename ) );
          }

        } else {
          // let's check parent folder
          FileObject parentFolder = data.targetFile.getParent();
          if ( !parentFolder.exists() ) {
            if ( !meta.isCreateParentFolder() ) {
              throw new KettleException( BaseMessages.getString(
                PKG, "ProcessFiles.Error.TargetParentFolderNotExists", parentFolder.toString() ) );
            } else {
              parentFolder.createFolder();
            }
          }
          if ( parentFolder != null ) {
            parentFolder.close();
          }
        }

View on GitHub (pinned to f3058517a1)