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.

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)

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.