OpenFeign/feign · error · FeignException
reading (response body)
Error message
%s reading %s %s (response body)
What it means
In InvocationContext.proceed, when the response status should NOT have its body decoded (status outside 2xx, or 404 without dismiss404 on a non-void type), decodeError is invoked to produce the exception from the ErrorDecoder, whose message is formatted "%s reading %s %s (response body)". It signals the library refused to decode a body because the response was not a decodable success (or dismissible 404).
Solutions
- Read the FeignException's status and content from the ErrorDecoder to see the server's error payload.
- Install a custom ErrorDecoder to map expected statuses to domain exceptions or defaults.
- Enable dismiss404 (404 -> null) via options if 404 is an acceptable outcome for non-void methods.
- Fix the client-side cause (bad id, wrong URL, expired credentials) indicated by the status code.
Example fix
// before
Feign.builder().target(UserApi.class, url);
// after
Feign.builder()
.dismiss404()
.errorDecoder(new ErrorDecoder() {
public Exception decode(String methodKey, Response response) {
if (response.status() == 404) return new UserNotFoundException(methodKey);
return new Default().decode(methodKey, response);
}
})
.target(UserApi.class, url); Defensive patterns
Strategy: try-catch
Try / catch
try {
return api.getUser(id);
} catch (FeignException e) {
switch (e.status()) {
case 404: return Optional.empty();
case 429: throw new RateLimitedException(e);
default: throw e;
}
} Prevention
- Register a custom ErrorDecoder mapping expected statuses to domain exceptions
- Use dismiss404 when 404 is a valid empty result for non-void methods
- Log FeignException status and body for diagnosis
- Treat non-2xx as expected API outcomes and handle them per endpoint
When it happens
Trigger: Server returned 4xx/5xx (non-2xx) for a method call; server returned 404 while dismiss404 is false (or the method's return type is not void); a redirect/status the ErrorDecoder maps to an exception.
Common situations: Upstream API returning 500 during an outage; resource genuinely missing (404) on a typed endpoint; wrong base URL hitting an error page; rate limiting 429 responses.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- ${e.toString()}
- JAXB only supports decoding raw types. Found
- ${e.toString()}
- is not a type supported by this decoder.
- reading
AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10).
Data as JSON: /api/errors/eb2c035a9c50dfa9.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/feign/InvocationContext.java:80
return returnType;
}
public Response response() {
return response;
}
public Object proceed() throws Exception {
if (returnType == Response.class) {
return disconnectResponseBodyIfNeeded(response);
}
try {
final boolean shouldDecodeResponseBody =
(response.status() >= 200 && response.status() < 300)
|| (response.status() == 404 && dismiss404 && !isVoidType(returnType));
if (!shouldDecodeResponseBody) {
throw decodeError(configKey, response);
}
if (isVoidType(returnType) && !decodeVoid) {
ensureClosed(response.body());
return kotlinUnitInstance(returnType);
}
Class<?> rawType = Types.getRawType(returnType);
if (TypedResponse.class.isAssignableFrom(rawType)) {
Type bodyType = Types.resolveLastTypeParameter(returnType, TypedResponse.class);
return TypedResponse.builder(response).body(decode(response, bodyType)).build();
}
return decode(response, returnType);
} finally {
if (closeAfterDecode) {
ensureClosed(response.body());
}View on GitHub (pinned to e2a1e27560)