OpenFeign/feign · error · DecodeException

${jsonException.getMessage()}

Error message

${jsonException.getMessage()}

What it means

JsonDecoder.decodeBody() only supports decoding into String, Map, JSONObject, or JSONArray. For any other target Type it throws a DecodeException stating the type is not supported. This is a capability limitation of the org.json-based decoder, not a data problem.

Solutions

  1. Change the Feign method return type to Map<String, Object>, JSONObject, JSONArray, or String
  2. Switch to a data-binding decoder (JacksonDecoder/GsonDecoder) for POJOs
  3. Convert manually: decode to JSONObject/Map and map to your POJO yourself
  4. Wrap with a custom Decoder that delegates to JsonDecoder only for supported types

Example fix

// before
@RequestLine("GET /users/{id}") User getUser(String id); // JsonDecoder
// after
@RequestLine("GET /users/{id}") Map<String, Object> getUser(String id);
// or use Feign.builder().decoder(new JacksonDecoder())
Defensive patterns

Strategy: type-guard

Validate before calling

static boolean jsonDecoderSupported(Type t) {
  return t == String.class || Map.class.equals(t)
      || JSONObject.class.isAssignableFrom((Class<?>) t)
      || JSONArray.class.isAssignableFrom((Class<?>) t);
}

Type guard

boolean ok = jsonDecoderSupported(returnType); if (!ok) useJacksonDecoder();

Prevention

When it happens

Trigger: Declaring a Feign method returning a custom POJO, List<MyPojo>, Optional<X>, or any type other than String/Map/JSONObject/JSONArray while using JsonDecoder as the decoder.

Common situations: Swapping Jackson/Gson for JsonDecoder without realizing it has no reflection/data-binding; changing a Feign interface method's return type after switching decoders.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at json/src/main/java/feign/json/JsonDecoder.java:96

      bodyReader.reset();
      return decodeBody(response, type, bodyReader);
    } catch (JSONException jsonException) {
      if (jsonException.getCause() != null && jsonException.getCause() instanceof IOException) {
        throw (IOException) jsonException.getCause();
      }
      throw new DecodeException(
          response.status(), jsonException.getMessage(), response.request(), jsonException);
    }
  }

  private Object decodeBody(Response response, Type type, Reader reader) throws IOException {
    if (String.class.equals(type)) return Util.toString(reader);
    JSONTokener tokenizer = new JSONTokener(reader);
    if (Map.class.equals(type)) return new JSONObject(tokenizer).toMap();
    else if (JSONObject.class.isAssignableFrom((Class<?>) type)) return new JSONObject(tokenizer);
    else if (JSONArray.class.isAssignableFrom((Class<?>) type)) return new JSONArray(tokenizer);
    else
      throw new DecodeException(
          response.status(),
          format("%s is not a type supported by this decoder.", type),
          response.request());
  }

  @Override
  public Object convert(Object object, Type type) throws IOException {
    if (type instanceof Class) {
      Class<?> cls = (Class<?>) type;
      if (cls == JSONObject.class && object instanceof Map) {
        return new JSONObject((Map<?, ?>) object);
      }
      if (cls == String.class) {
        return object.toString();
      }
    }
    if (object instanceof Map) {
      return new JSONObject((Map<?, ?>) object);

View on GitHub (pinned to e2a1e27560)