apolloconfig/apollo · error · ServiceException

export items failed

Error message

export items failed:{}

What it means

ServiceException (HTTP 500) from ConfigsExportController single-namespace export when writing the generated config file content to the HttpServletResponse output stream fails. Note the constructor used is ServiceException(String, Exception), so the '{}' in 'export items failed:{}' is NOT formatted (it is Guava-agnostic); the caught Exception is attached as the cause and is where the real detail lives.

Solutions

  1. Read the attached cause Exception in the portal log (it is the real failure, not the '{}' placeholder).
  2. If it is ClientAbortException / broken pipe, treat as benign (the client left) and add a client-disconnect check before writing.
  3. Verify the connection/proxy timeouts are large enough for big namespaces.
  4. Avoid committing the response before the controller writes (no early flush).

Example fix

// before: any I/O failure becomes a 500 ServiceException
try {
  res.getOutputStream().write(configFileContent.getBytes());
} catch (Exception e) {
  throw new ServiceException("export items failed:{}", e);
}

// after: distinguish a client abort from a real failure, and fix the placeholder
try {
  res.getOutputStream().write(configFileContent.getBytes());
} catch (ClientAbortException e) {
  log.warn("client aborted export for {}", fileName);
} catch (IOException e) {
  throw new ServiceException("export items failed for %s", e, fileName);
}
Defensive patterns

Strategy: try-catch

Try / catch

// Export can 500 on client disconnect or backend I/O failure.
try {
  portal.exportNamespace(appId, env, cluster, namespace); // downloads file
} catch (HttpServerErrorException e) {
  String body = e.getResponseBodyAsString();
  if (body.contains("export items failed")) {
    // most often a dropped connection; retry once, or surface 'download failed'
    log.warn("namespace export failed, possible client disconnect: {}", body);
  }
  throw e;
}

Prevention

When it happens

Trigger: GET /apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/export (single-file export) when res.getOutputStream().write(bytes) throws — client disconnected, broken pipe, container I/O error, or the response is already committed.

Common situations: User cancels the download mid-stream (ClientAbortException); a proxy/load-balancer timing out and closing the connection; the namespace is large and the socket drops; response already committed by a filter.

Related errors


AI-assisted analysis of apolloconfig/apollo@d95fc18d11 (2026-08-14). Data as JSON: /api/errors/2f7072d79c040f01. Report an issue: GitHub.

Appendix: source

Thrown at apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConfigsExportController.java:111

    // properties file or public namespace has not suffix (.properties)
    if (fileNameSplit.size() <= 1
        || !ConfigFileFormat.isValidFormat(fileNameSplit.get(fileNameSplit.size() - 1))) {
      fileName = Joiner.on(".").join(namespaceName, ConfigFileFormat.Properties.getValue());
    }

    NamespaceBO namespaceBO = namespaceService.loadNamespaceBO(appId, Env.valueOf(env), clusterName,
        namespaceName, true, false);

    // generate a file.
    res.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName);
    // file content
    final String configFileContent = NamespaceBOUtils.convert2configFileContent(namespaceBO);
    try {
      // write content to net
      res.getOutputStream().write(configFileContent.getBytes());
    } catch (Exception e) {
      throw new ServiceException("export items failed:{}", e);
    }
  }

  /**
   * Export all configs in a compressed file. Just export namespace which current exists read permission. The permission
   * check in service.
   */
  @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()")
  @GetMapping("/configs/export")
  public void exportAll(@RequestParam(value = "envs") String envs, HttpServletRequest request,
      HttpServletResponse response) throws IOException {
    // filename must contain the information of time
    final String filename =
        "apollo_config_export_" + DateFormatUtils.format(new Date(), "yyyy_MMdd_HH_mm_ss") + ".zip";
    // log who download the configs
    logger.info("Download configs, remote addr [{}], remote host [{}]. Filename is [{}]",
        request.getRemoteAddr(), request.getRemoteHost(), filename);
    // set downloaded filename

View on GitHub (pinned to d95fc18d11)