OpenFeign/feign · error · DecodeException

${e.getMessage()}

Error message

${e.getMessage()}

What it means

Thrown as a DecodeException wrapping the original decoder failure when the configured Decoder throws a non-Feign RuntimeException while deserializing the response body into the declared return type. It fires in InvocationContext.decode after the HTTP call succeeded, meaning the response arrived but could not be mapped — commonly a JSON/POJO mismatch (unknown properties, wrong type shape) or a decoder misconfiguration for the method's return Type.

Solutions

  1. Verify the interface method's return type matches the actual response body structure (field names, nesting, generics)
  2. Configure the underlying codec (e.g., Jackson ObjectMapper) to match the payload, or use a lenient/fail-safe setting if appropriate
  3. Ensure the correct Decoder is installed for the response content type (e.g., Jackson vs Gson)
  4. Catch DecodeException at the call site to distinguish transport success from deserialization failure
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at core/src/main/java/feign/InvocationContext.java:125 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at core/src/main/java/feign/InvocationContext.java:125

    if (!shouldDisconnectResponseBody) {
      return response;
    }

    try {
      final byte[] bodyData = Util.toByteArray(response.body().asInputStream());
      return response.toBuilder().body(bodyData).build();
    } finally {
      ensureClosed(response.body());
    }
  }

  private Object decode(Response response, Type returnType) {
    try {
      return decoder.decode(response, returnType);
    } catch (final FeignException e) {
      throw e;
    } catch (final RuntimeException e) {
      throw new DecodeException(response.status(), e.getMessage(), response.request(), e);
    } catch (IOException e) {
      throw errorReading(response.request(), response, e);
    }
  }

  private Exception decodeError(String methodKey, Response response) {
    try {
      return errorDecoder.decode(methodKey, response);
    } finally {
      ensureClosed(response.body());
    }
  }

  private boolean isVoidType(Type returnType) {
    return returnType == Void.class
        || returnType == void.class
        || returnType.getTypeName().equals("kotlin.Unit");
  }

View on GitHub (pinned to e2a1e27560)