{"record":{"id":"a4ff1afdc404d895","repo":"apolloconfig/apollo","slug":"write-error","errorCode":null,"errorMessage":"Write error. {}","messagePattern":"Write error\\. (.+?)","errorType":"exception","errorClass":"ServiceException","httpStatus":500,"severity":"error","filePath":"apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ConfigsExportService.java","lineNumber":325,"sourceCode":"      }\n    };\n\n    configBOStream.forEach(configBOConsumer);\n  }\n\n  private void writeClusterInfoToZip(Env env, App app, List<ClusterDTO> exportClusters,\n      ZipOutputStream zipOutputStream) {\n    final Consumer<ClusterDTO> clusterConsumer = cluster -> {\n      try {\n        synchronized (zipOutputStream) {\n          String fileName = ConfigFileUtils.genClusterInfoPath(app, env, cluster);\n          String content = gson.toJson(cluster);\n\n          writeToZip(fileName, content, zipOutputStream);\n        }\n      } catch (IOException e) {\n        logger.error(\"Write error. {}\", cluster);\n        throw new ServiceException(\"Write error. {}\", e);\n      }\n    };\n\n    exportClusters.forEach(clusterConsumer);\n  }\n\n  private void writeToZip(String filePath, String content, ZipOutputStream zipOutputStream)\n      throws IOException {\n    final ZipEntry zipEntry = new ZipEntry(filePath);\n    try {\n      zipOutputStream.putNextEntry(zipEntry);\n      zipOutputStream.write(content.getBytes());\n      zipOutputStream.closeEntry();\n    } catch (IOException e) {\n      String errorMsg = \"write content to zip error. file = \" + filePath + \", content = \" + content;\n      logger.error(errorMsg);\n      throw new IOException(errorMsg, e);\n    }","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/apolloconfig/apollo/blob/d95fc18d112589efc09ddcbe1507047584d55251/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ConfigsExportService.java#L307-L343","documentation":"Thrown by ConfigsExportService.writeClusterInfoToZip() when serializing a ClusterDTO to JSON and writing it as a ZipEntry fails with an IOException. The cluster consumer iterates over exportClusters, generates a cluster-info file path, and calls writeToZip inside a synchronized block on the ZipOutputStream. A single IOException aborts the whole cluster export via ServiceException.","triggerScenarios":"Exporting cluster metadata where writeToZip() encounters a closed/finished ZipOutputStream, a client-disconnected response stream, or a disk-full condition. The synchronized block ensures thread safety but any I/O failure inside it throws and propagates out of forEach.","commonSituations":"Client cancels the export download; portal container disk full; ZipOutputStream.finish() invoked early by a prior error handler; concurrent export of the same env producing interleaved close/write.","solutions":["Review portal logs for the 'Write error. {cluster}' line to identify the failing ClusterDTO and the IOException cause.","Confirm the export ZipOutputStream lifecycle: it must remain open until all consumers (app, namespace, cluster) finish.","Check server disk space and client connection stability during large multi-cluster exports.","Ensure exportClusters is not invoked on an already-finished or closed stream from a prior failed export stage."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  configsExportService.writeClusterInfoToZip(env, app, clusters, zipOutputStream);\n} catch (ServiceException e) {\n  log.error(\"Cluster info write to zip failed\", e);\n  throw e;\n}","preventionTips":["Validate the ZipOutputStream is not finished or closed before calling writeClusterInfoToZip.","Ensure cluster export runs as part of a single open-stream lifecycle.","Handle client disconnects gracefully to avoid partial zip corruption."],"tags":["apollo-portal","config-export","io","zip","cluster","service-exception"],"backgroundTag":null,"analyzedSha":"d95fc18d112589efc09ddcbe1507047584d55251","analyzedAt":"2026-08-14T04:00:05.477Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}