{"record":{"id":"20643dca940221f1","repo":"pentaho/pentaho-kettle","slug":"already-closed-jcrfileoutputstream","errorCode":null,"errorMessage":"Already closed","messagePattern":"Already closed","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"plugins/repo-vfs/repo-vfs-ws/src/main/java/org/pentaho/di/plugins/repovfs/ws/vfs/JCRFileOutputStream.java","lineNumber":44,"sourceCode":" */\npublic class JCRFileOutputStream extends ByteArrayOutputStream {\n\n  private static final Logger log = LoggerFactory.getLogger( JCRFileOutputStream.class );\n\n  private final RepositoryClient client;\n  private final String[] name;\n\n  private boolean closed = false;\n\n  public JCRFileOutputStream( String[] fileName, RepositoryClient client ) {\n    this.name = fileName;\n    this.client = client;\n  }\n\n  @Override\n  public void close() throws IOException {\n    if ( closed ) {\n      throw new IOException( new IllegalStateException( \"Already closed\" ) );\n    }\n\n    closed = true;\n    try ( ByteArrayInputStream bais = new ByteArrayInputStream( toByteArray() ) ) {\n      client.writeData( name, bais );\n      log.debug( \"data written\" );\n    } catch ( RepositoryClientException e ) {\n      throw new IOException( e );\n    }\n  }\n\n}\n","sourceCodeStart":26,"sourceCodeEnd":57,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/plugins/repo-vfs/repo-vfs-ws/src/main/java/org/pentaho/di/plugins/repovfs/ws/vfs/JCRFileOutputStream.java#L26-L57","documentation":"JCRFileOutputStream.close writes the buffered data to the repository via client.writeData. If close() is invoked a second time after the stream was already closed, it throws an IOException wrapping an IllegalStateException('Already closed'). This guards against double-write of the stream's buffer to the repository.","triggerScenarios":"Calling close() twice on the same JCRFileOutputStream, or code paths (e.g. try-with-resources plus an explicit close in a finally block) that close the stream more than once.","commonSituations":"try-with-resources combined with a manual close(); utility code that closes streams defensively; reusing a cached output stream reference after it has been committed.","solutions":["Remove duplicate close() calls; rely on a single close path (prefer try-with-resources alone)","Check the stream's own guard if you must double-close defensively","Wrap close() in an ignored-IOException guard only when the close is best-effort"],"exampleFix":"// before\nstream.close();\n...\nstream.close(); // second close throws 'Already closed'\n// after\nstream.close();\n...\nif ( !stream.isClosed() ) { stream.close(); } // or remove the duplicate close entirely","handlingStrategy":"try-catch","validationCode":"// guard before closing defensively\nboolean streamIsOpen( OutputStream s ) { return s != null && !( s instanceof JCRFileOutputStream j && j.isClosed() ); }","typeGuard":"boolean canClose( OutputStream s ) {\n  return s instanceof JCRFileOutputStream && !((JCRFileOutputStream) s).isClosed();\n}","tryCatchPattern":"try ( OutputStream out = client.getOutputStream( name ) ) {\n  out.write( data );\n} catch ( IOException e ) {\n  if ( e.getCause() instanceof IllegalStateException\n       && \"Already closed\".equals( e.getCause().getMessage() ) ) {\n    log.debug( \"Stream already closed; ignoring\" );\n  } else {\n    throw e;\n  }\n}","preventionTips":["Use a single close path: try-with-resources only, no manual close() in finally","Never cache or reuse output stream objects after committing data","Check closed state before defensive closes in utility code"],"tags":["io","stream","double-close"],"backgroundTag":"file-write-failed","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"}