pentaho/pentaho-kettle · error · KettleException

ProcessFiles.Error.SourceFileNotFile

Error message

ProcessFiles.Error.SourceFileNotFile

What it means

Thrown when the source path resolves but is not a regular file - e.g. it is a directory or other VFS FileType. Pentaho requires the source to be a real file because copy/move/delete semantics on folders are not supported by this step.

Solutions

  1. Point the source filename field to regular files, not directories.
  2. In 'Get File Names', enable 'Do not include folder name' / set 'Files/Folders' to 'Files only' or set 'Include subfolders' with a file filter.
  3. For directory processing, use a 'Get File Names' step to expand the directory into individual file rows first.
  4. Trim the path string upstream if a stray separator or folder segment was appended.

Example fix

// before
GetFileNames(includeFolders=true) -> ProcessFiles
// after
GetFileNames(filesOnly=true, filter='*.txt') -> ProcessFiles
Defensive patterns

Strategy: validation

Validate before calling

// Guard before ProcessFiles:
new java.io.File(sourceFilename).isFile()

Type guard

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

Try / catch

try {
  processRow();
} catch (KettleException e) {
  if (e.getMessage().contains("SourceFileNotFile")) {
    logError("Source path is a directory, not a file");
  } else throw e;
}

Prevention

When it happens

Trigger: data.sourceFile.getType() != FileType.FILE after resolving sourceFilename; typically the field contains a directory path.

Common situations: Upstream 'Get File Names' step configured to include subfolder/folder names; user passed a directory expecting recursive processing; trailing path points to a folder after an upstream transformation of the path string.

Related errors


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

Appendix: source

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

    } // End If first
    try {
      // get source filename
      String sourceFilename = getInputRowMeta().getString( r, data.indexOfSourceFilename );

      if ( Utils.isEmpty( sourceFilename ) ) {
        logError( BaseMessages.getString( PKG, "ProcessFiles.Error.SourceFileEmpty" ) );
        throw new KettleException( BaseMessages.getString( PKG, "ProcessFiles.Error.SourceFileEmpty" ) );
      }
      data.sourceFile = KettleVFS.getInstance( getTransMeta().getBowl() ).getFileObject( sourceFilename, getTransMeta() );

      if ( !data.sourceFile.exists() ) {
        logError( BaseMessages.getString( PKG, "ProcessFiles.Error.SourceFileNotExist", sourceFilename ) );
        throw new KettleException( BaseMessages.getString(
          PKG, "ProcessFiles.Error.SourceFileNotExist", sourceFilename ) );
      }
      if ( data.sourceFile.getType() != FileType.FILE ) {
        logError( BaseMessages.getString( PKG, "ProcessFiles.Error.SourceFileNotFile", sourceFilename ) );
        throw new KettleException( BaseMessages.getString(
          PKG, "ProcessFiles.Error.SourceFileNotFile", sourceFilename ) );
      }
      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 ) {

View on GitHub (pinned to f3058517a1)