pentaho/pentaho-kettle · error · KettleException

JobMssqlBulkLoad.Error.OnlyLocalFileSupported

Error message

JobMssqlBulkLoad.Error.OnlyLocalFileSupported

What it means

KettleException thrown during JobEntryMssqlBulkLoad.execute() when the configured filename resolves through KettleVFS to something other than a LocalFile. MS SQL Server's BULK INSERT can only read files local to the database server, so the entry deliberately rejects non-local VFS objects.

Solutions

  1. Change the filename to a local path on the SQL Server host (e.g. C:\data\file.csv) so KettleVFS resolves a LocalFile.
  2. Copy the remote file to the database server first (e.g. with a preceding job entry or script) and bulk-load the local copy.
  3. Use a staging directory accessible as a local disk on the DB server, then reference it in the entry.
  4. If remote loading is required, use a different mechanism (e.g. SSIS, BACPAC, or streaming inserts) instead of BULK INSERT.

Example fix

// before
<filename>sftp://host/data/import.csv</filename>
// after
<filename>C:\data\import.csv</filename>
Defensive patterns

Strategy: validation

Validate before calling

// before execute
String resolved = environmentSubstitute( filename );
if ( resolved.startsWith( "sftp://" ) || resolved.startsWith( "http://" ) || resolved.startsWith( "hdfs://" ) ) {
  throw new KettleValidationException( "MSSQL BULK INSERT requires a local file path, got: " + resolved );
}

Type guard

boolean isLocalPath = (String s) -> s != null && !s.contains( "://" );

Try / catch

try { result = jobEntry.execute( prevResult, nr ); } catch ( KettleException e ) { if ( e.getMessage().contains( "OnlyLocalFileSupported" ) ) { /* stage file locally on DB server, then retry */ } }

Prevention

When it happens

Trigger: Running the 'MSsql bulk load' job entry with a filename that resolves to a remote VFS scheme — e.g. sftp://, http://, hdfs://, or another non-LocalFile FileObject — instead of a plain local path.

Common situations: User points the entry at an SFTP or HTTP URL assuming MSSQL can read it; clustered/multi-server setups where the file exists on one node but not the SQL Server host; environment variable or parameter resolving to a remote location.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/job/entries/mssqlbulkload/JobEntryMssqlBulkLoad.java:377

    Result result = previousResult;
    result.setResult( false );

    String vfsFilename = environmentSubstitute( filename );
    FileObject fileObject = null;
    // Let's check the filename ...
    if ( !Utils.isEmpty( vfsFilename ) ) {
      try {
        // User has specified a file, We can continue ...
        //
        // This is running over VFS but we need a normal file.
        // As such, we're going to verify that it's a local file...
        // We're also going to convert VFS FileObject to File
        //
        fileObject = KettleVFS.getInstance( parentJobMeta.getBowl() ).getFileObject( vfsFilename, this );
        if ( !( fileObject instanceof LocalFile ) ) {
          // MSSQL BUKL INSERT can only use local files, so that's what we limit ourselves to.
          //
          throw new KettleException( BaseMessages.getString(
            PKG, "JobMssqlBulkLoad.Error.OnlyLocalFileSupported", vfsFilename ) );
        }

        // Convert it to a regular platform specific file name
        //
        String realFilename = KettleVFS.getFilename( fileObject );

        // Here we go... back to the regular scheduled program...
        //
        File file = new File( realFilename );
        if ( file.exists() && file.canRead() ) {
          // User has specified an existing file, We can continue ...
          if ( log.isDetailed() ) {
            logDetailed( BaseMessages.getString( PKG, "JobMssqlBulkLoad.FileExists.Label", realFilename ) );
          }

          if ( connection != null ) {
            // User has specified a connection, We can continue ...

View on GitHub (pinned to f3058517a1)