{"record":{"id":"68dcde2d7c627ec8","repo":"pentaho/pentaho-kettle","slug":"jobmssqlbulkload-error-onlylocalfilesupported","errorCode":null,"errorMessage":"JobMssqlBulkLoad.Error.OnlyLocalFileSupported","messagePattern":"JobMssqlBulkLoad\\.Error\\.OnlyLocalFileSupported","errorType":"exception","errorClass":"KettleException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/job/entries/mssqlbulkload/JobEntryMssqlBulkLoad.java","lineNumber":377,"sourceCode":"    Result result = previousResult;\n    result.setResult( false );\n\n    String vfsFilename = environmentSubstitute( filename );\n    FileObject fileObject = null;\n    // Let's check the filename ...\n    if ( !Utils.isEmpty( vfsFilename ) ) {\n      try {\n        // User has specified a file, We can continue ...\n        //\n        // This is running over VFS but we need a normal file.\n        // As such, we're going to verify that it's a local file...\n        // We're also going to convert VFS FileObject to File\n        //\n        fileObject = KettleVFS.getInstance( parentJobMeta.getBowl() ).getFileObject( vfsFilename, this );\n        if ( !( fileObject instanceof LocalFile ) ) {\n          // MSSQL BUKL INSERT can only use local files, so that's what we limit ourselves to.\n          //\n          throw new KettleException( BaseMessages.getString(\n            PKG, \"JobMssqlBulkLoad.Error.OnlyLocalFileSupported\", vfsFilename ) );\n        }\n\n        // Convert it to a regular platform specific file name\n        //\n        String realFilename = KettleVFS.getFilename( fileObject );\n\n        // Here we go... back to the regular scheduled program...\n        //\n        File file = new File( realFilename );\n        if ( file.exists() && file.canRead() ) {\n          // User has specified an existing file, We can continue ...\n          if ( log.isDetailed() ) {\n            logDetailed( BaseMessages.getString( PKG, \"JobMssqlBulkLoad.FileExists.Label\", realFilename ) );\n          }\n\n          if ( connection != null ) {\n            // User has specified a connection, We can continue ...","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/job/entries/mssqlbulkload/JobEntryMssqlBulkLoad.java#L359-L395","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n<filename>sftp://host/data/import.csv</filename>\n// after\n<filename>C:\\data\\import.csv</filename>","handlingStrategy":"validation","validationCode":"// before execute\nString resolved = environmentSubstitute( filename );\nif ( resolved.startsWith( \"sftp://\" ) || resolved.startsWith( \"http://\" ) || resolved.startsWith( \"hdfs://\" ) ) {\n  throw new KettleValidationException( \"MSSQL BULK INSERT requires a local file path, got: \" + resolved );\n}","typeGuard":"boolean isLocalPath = (String s) -> s != null && !s.contains( \"://\" );","tryCatchPattern":"try { result = jobEntry.execute( prevResult, nr ); } catch ( KettleException e ) { if ( e.getMessage().contains( \"OnlyLocalFileSupported\" ) ) { /* stage file locally on DB server, then retry */ } }","preventionTips":["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"],"tags":["kettle","vfs","mssql","bulk-load","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}