pentaho/pentaho-kettle · error · KettleXMLException

Unable to close file '" + filename + "'

Error message

Unable to close file '" + filename + "'

What it means

In TransMeta.saveToKtr, the finally block closes the FileOutputStream; if fos.close() throws IOException it is wrapped in KettleXMLException 'Unable to close file <filename>'. The bytes may already be on disk but the file handle could not be cleanly released, so the file may be incomplete or unlocked only later.

Solutions

  1. Check the wrapped IOException cause; verify disk space, since close flushes remaining bytes.
  2. Save to a local temp file and move/replace the target atomically after a successful close.
  3. Retry the save after excluding the path from AV/indexing lockers.
  4. Prefer Java 7+ try-with-resources semantics or upgrade PDI so close is handled without masking the primary error.

Example fix

// before
transMeta.saveToKtr("/mnt/share/mytrans.ktr"); // flaky network share
// after
File tmp = File.createTempFile("mytrans", ".ktr");
transMeta.saveToKtr(tmp.getAbsolutePath());
Files.move(tmp.toPath(), Paths.get("/mnt/share/mytrans.ktr"),
  StandardCopyOption.REPLACE_EXISTING);
Defensive patterns

Strategy: fallback

Validate before calling

File target = new File(filename);
if (target.getFreeSpace() < 1_000_000L) throw new IllegalStateException("Low disk space before save");

Try / catch

try {
  transMeta.saveToKtr(filename);
} catch (KettleXMLException e) {
  if (String.valueOf(e.getCause()).contains("close")) {
    // save to temp then atomic move, or retry after checking disk space
  }
}

Prevention

When it happens

Trigger: saveToKtr() reaching the finally clause where fos.close() throws IOException — typically OS-level flush failure (disk full at flush time), stream already closed, or underlying file system glitch.

Common situations: Disk filled up between write and close so buffered bytes cannot flush; file locked by another process/AV scanner; saving to a network share that dropped mid-write.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/TransMeta.java:6604

   * @param filename
   *          The filename to save to
   * @throws KettleXMLException
   *           in case something goes wrong.
   */
  public void writeXML( String filename ) throws KettleXMLException {
    FileOutputStream fos = null;
    try {
      fos = new FileOutputStream( filename );
      fos.write( XMLHandler.getXMLHeader().getBytes( Const.XML_ENCODING ) );
      fos.write( getXML().getBytes( Const.XML_ENCODING ) );
    } catch ( Exception e ) {
      throw new KettleXMLException( "Unable to save to XML file '" + filename + "'", e );
    } finally {
      if ( fos != null ) {
        try {
          fos.close();
        } catch ( IOException e ) {
          throw new KettleXMLException( "Unable to close file '" + filename + "'", e );
        }
      }
    }
  }

  /**
   * Checks whether the transformation has repository references.
   *
   * @return true if the transformation has repository references, false otherwise
   */
  public boolean hasRepositoryReferences() {
    for ( StepMeta stepMeta : steps ) {
      if ( stepMeta.getStepMetaInterface().hasRepositoryReferences() ) {
        return true;
      }
    }
    return false;
  }

View on GitHub (pinned to f3058517a1)