pentaho/pentaho-kettle · error · KettleException

ExcelInput.Log.UnsafeFileException

ExcelInput.Log.UnsafeFileException

Error message

ExcelInput.Log.UnsafeFileException (localized message) + e

What it means

prepareNextOutputFile() specifically catches IOException and POIXMLException when opening the new Excel output file and throws a KettleException containing the localized 'ExcelInput.Log.UnsafeFileException' message concatenated with the exception. This signals the output file could not be safely opened/written by POI.

Solutions

  1. Ensure the target file is not open/locked in Excel or another process
  2. If appending, confirm the existing file is a genuine workbook matching its extension
  3. Delete/replace a corrupt output file and rerun
  4. Check disk space and write permission on the output directory
  5. Read the appended exception text in the message for the exact POI/IO cause

Example fix

// before: file renamed to .xlsx but actually old .xls content
// after: re-save the file in the correct format (xlsx) or change the step's Extension setting to xls
Defensive patterns

Strategy: try-catch

Validate before calling

File f = new File(filename);
if (f.exists() && f.length() == 0) { /* safe to overwrite */ }
else if (f.exists()) {
  try (InputStream in = new FileInputStream(f)) {
    byte[] magic = new byte[4]; in.read(magic);
    boolean isZip = (magic[0] == 'P' && magic[1] == 'K'); // xlsx
    boolean isOle2 = (magic[0] == (byte)0xD0 && magic[1] == (byte)0xCF); // xls
    // ensure magic matches the configured extension
  }
}

Try / catch

try { writer.prepareNextOutputFile(); }
catch (KettleException e) {
  if (e.getCause() instanceof IOException || e.getCause() instanceof POIXMLException) {
    logError("Unsafe/locked/corrupt output file: " + filename, e);
  }
  setErrors(1);
  throw e;
}

Prevention

When it happens

Trigger: IOException creating the output stream or file; POIXMLException from POI when constructing the workbook (corrupt template, mismatched OLE2/OOXML content, zip errors in an xlsx).

Common situations: Appending to a file whose content is not a valid workbook (e.g. it is actually HTML or a renamed file); template copied in place is corrupt; output path is a directory or locked by Excel having the file open; extension says xlsx but content is xls.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at plugins/excel/core/src/main/java/org/pentaho/di/trans/steps/excelwriter/ExcelWriterStep.java:778

      // may have to write a header here
      if ( meta.isHeaderEnabled() && !( !data.createNewSheet && meta.isAppendOmitHeader() && appendingToSheet ) ) {
        writeHeader();
      }

      // If it's to use streaming, initialize it now as we already made all necessary initial calculations.
      if ( data.wb instanceof XSSFWorkbook && meta.isStreamingData() ) {
        data.innerSheet = Optional.of( data.sheet );
        data.wb = new SXSSFWorkbook( (XSSFWorkbook) data.wb, STREAMING_WINDOW_SIZE );
        data.sheet = data.wb.getSheet( data.realSheetname );
      }

      if ( log.isDebug() ) {
        logDebug( BaseMessages.getString( PKG, "ExcelWriterStep.Log.FileOpened", buildFilename ) );
      }
      // this is the number of the new output file
      data.splitnr++;
    }  catch ( IOException | POIXMLException e ) {
        throw new KettleException( BaseMessages.getString( PKG, "ExcelInput.Log.UnsafeFileException" ) +
                e
        );
    }
    catch ( Exception e ) {
      logError( "Error opening new file", e );
      setErrors( 1 );
      throw new KettleException( e );
    }
  }

  /** Sets data.startingRow, data.startingCol, data.posX, data.posY */
  private void setSheetPosition( boolean appendingToSheet ) {
    // starting cell support
    if ( !Utils.isEmpty( data.realStartingCell ) ) {
      CellReference cellRef = new CellReference( data.realStartingCell );
      data.startingRow = cellRef.getRow();
      data.startingCol = cellRef.getCol();
    } else {

View on GitHub (pinned to f3058517a1)