{"record":{"id":"f134126e68b99a4a","repo":"pentaho/pentaho-kettle","slug":"can-not-append-to-an-existing-zip-file-filename","errorCode":null,"errorMessage":"Can not append to an existing zip file : {filename}","messagePattern":"Can not append to an existing zip file : (.+?)","errorType":"exception","errorClass":"KettleException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/trans/steps/textfileoutput/TextFileOutput.java","lineNumber":157,"sourceCode":"        boolean writingToFileForFirstTime = fileStreams == null;\n\n        if ( writingToFileForFirstTime ) { // Opening file for first time\n\n          if ( meta.isAddToResultFiles() ) {\n            // Add this to the result file names...\n            ResultFile resultFile =\n              new ResultFile( ResultFile.FILE_TYPE_GENERAL, getFileObject( filename, getTransMeta() ),\n                getTransMeta().getName(), getStepname() );\n            resultFile.setComment( BaseMessages.getString( PKG, \"TextFileOutput.AddResultFile\" ) );\n            addResultFile( resultFile );\n          }\n\n          CompressionProvider compressionProvider = getCompressionProvider();\n          boolean isZipFile = compressionProvider instanceof ZIPCompressionProvider;\n          boolean appendToExistingFile = meta.isFileAppended();\n\n          if ( appendToExistingFile && isZipFile && isFileExists( filename ) ) {\n            throw new KettleException( \"Can not append to an existing zip file : \" + filename );\n          }\n\n          int maxOpenFiles = getMaxOpenFiles();\n          if ( ( maxOpenFiles > 0 ) && ( data.getFileStreamsCollection().getNumOpenFiles() >= maxOpenFiles ) ) {\n            // If the file we're going to close is a zip file,  going to remove it from the collection of files\n            // that have been opened. We do this because it is not possible to reopen a\n            // zip file for append. By removing it from the collection, if the same file is referenced later, it will look\n            // like we're opening the file for the first time, and if we're set up to append to existing files it will cause and\n            // exception to be thrown, which is the desired result.\n            data.getFileStreamsCollection().closeOldestOpenFile( isZipFile );\n          }\n\n          if ( meta.isCreateParentFolder()\n            && ( ( data.getFileStreamsCollection().size() == 0 ) || meta.isFileNameInField() ) ) {\n            createParentFolder( filename );\n          }\n          if ( log.isDetailed() ) {\n            logDetailed( \"Opening output stream using provider: \" + compressionProvider.getName() );","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/trans/steps/textfileoutput/TextFileOutput.java#L139-L175","documentation":"initFileStreamWriter refuses to append to a ZIP-compressed target: ZIP archives cannot be reopened for appending, so when 'Append' is enabled on the Text File Output step, the compression provider is the ZIP provider, and the target file already exists, a KettleException 'Can not append to an existing zip file : <filename>' is thrown before any stream is opened.","triggerScenarios":"meta.isFileAppended() is true AND compression is ZIP (getCompressionProvider() instanceof ZIPCompressionProvider) AND isFileExists(filename) is true — i.e. a rerun of a transformation that already produced the zip file with append enabled.","commonSituations":"Scheduling a transformation to run repeatedly with 'Append to existing file' checked but compression set to Zip; leftover output from a previous failed run sits in the target directory; migrating a step from uncompressed/GZip append to Zip append without clearing old output.","solutions":["Disable the 'Append' option in the Text File Output step, or delete/move the existing .zip file before running.","Switch compression to None or GZip if appending is required (GZip supports append via stream concatenation).","Add a preceding step/job entry that removes the existing file when present.","Point the output at a new filename (e.g. include a timestamp) so the target does not already exist."],"exampleFix":"// before\ncompression = \"Zip\", append = true, filename = \"out.zip\" // out.zip exists\n// after\ncompression = \"Zip\", append = false\n// or: delete out.zip (or use compression \"GZip\" with append = true)","handlingStrategy":"validation","validationCode":"boolean isZip = \"Zip\".equalsIgnoreCase( meta.getCompressionType() );\nif ( isZip && meta.isFileAppended() && new File( filename ).exists() ) {\n  // delete, rename, or disable append before running\n  new File( filename ).delete();\n}","typeGuard":null,"tryCatchPattern":"try {\n  transformation.startExecution();\n} catch ( KettleException e ) {\n  if ( e.getMessage().startsWith( \"Can not append to an existing zip file\" ) ) {\n    // remove the zip or turn off append, then re-run\n  }\n}","preventionTips":["Never combine Append=true with Zip compression in Text File Output","Pre-clean stale output files in scheduled jobs before re-running","Use GZip or no compression when append semantics are required"],"tags":["kettle","pdi","zip","append"],"backgroundTag":"file-already-exists","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"}