OpenFeign/feign · error · FeignException

reading

Error message

%s reading %s %s

What it means

In ResponseHandler.handleResponse, an IOException raised while reading/decoding the response is logged (if logLevel != NONE) and rethrown as a FeignException built by errorReading with message "%s reading %s %s". Any other exception triggers ensureClosed(response.body()) and propagates. It indicates the response arrived but its body could not be read or processed.

Solutions

  1. Check the wrapped IOException cause: premature EOF points to connection/proxy issues, timeout points to slow body transfer.
  2. Increase read timeout in Request.Options for large payloads.
  3. Verify proxy/LB idle timeouts are larger than the expected body transfer time.
  4. Ensure the response body is read exactly once and Content-Encoding headers match the actual payload.

Example fix

// before
new Request.Options(2000, 2000);
// after
new Request.Options(5, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true);
Defensive patterns

Strategy: retry

Try / catch

try {
  return api.download();
} catch (FeignException e) {
  if (e.getCause() instanceof IOException io && isTransient(io)) {
    return retryWithBackoff(() -> api.download());
  }
  throw e;
}

Prevention

When it happens

Trigger: Connection closed mid-body read (truncated response); body stream IO failure while the decoder consumes it; decompression failure on gzip-encoded bodies; reading a body after the connection was already released.

Common situations: Load balancer or proxy terminating the connection early; very large responses hitting socket timeouts mid-read; misconfigured gzip; calling response.body() again after a prior read.

Related errors


AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10). Data as JSON: /api/errors/9cc275e96c1ee728. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/feign/ResponseHandler.java:83

  public Object handleResponse(
      String configKey, Response response, Type returnType, long elapsedTime) throws Exception {
    try {
      response = logAndRebufferResponseIfNeeded(configKey, response, elapsedTime);
      return executionChain.next(
          new InvocationContext(
              configKey,
              decoder,
              errorDecoder,
              dismiss404,
              closeAfterDecode,
              decodeVoid,
              response,
              returnType));
    } catch (final IOException e) {
      if (logLevel != Level.NONE) {
        logger.logIOException(configKey, logLevel, e, elapsedTime);
      }
      throw errorReading(response.request(), response, e);
    } catch (Exception e) {
      ensureClosed(response.body());
      throw e;
    }
  }

  private Response logAndRebufferResponseIfNeeded(
      String configKey, Response response, long elapsedTime) throws IOException {
    if (logLevel == Level.NONE) {
      return response;
    }

    return logger.logAndRebufferResponse(configKey, logLevel, response, elapsedTime);
  }
}

View on GitHub (pinned to e2a1e27560)