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
- Check the wrapped IOException cause; verify disk space, since close flushes remaining bytes.
- Save to a local temp file and move/replace the target atomically after a successful close.
- Retry the save after excluding the path from AV/indexing lockers.
- 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
- Save to a local temp file and atomically move into place
- Monitor disk space on save targets
- Avoid saving directly to flaky network shares or AV-scanned directories
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
- ChangeFileEncoding.Error.CreatingFile
- ex.getMessage()
- Exporting jobs: Couldn't create file
- Exporting transformation: Couldn't create file
- GPLoadDataOutput.Exception" + e.getMessage()
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)