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
- Change the filename to a local path on the SQL Server host (e.g. C:\data\file.csv) so KettleVFS resolves a LocalFile.
- Copy the remote file to the database server first (e.g. with a preceding job entry or script) and bulk-load the local copy.
- Use a staging directory accessible as a local disk on the DB server, then reference it in the entry.
- 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
- Always use local paths for MSSQL bulk load filenames
- Stage remote files onto the DB server before bulk loading
- Remember BULK INSERT reads files local to SQL Server, not the Kettle host
- Test resolved (variable-substituted) paths before running jobs
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
- CsvInput.Log.OnlyLocalFilesAreSupported
- Append file in repository is not possible
- [ + ArgList[0] + ] is not a file!
- AvroInputDialog.Error.KettleFileException
- Cannot add converters
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)