OpenFeign/feign · error · EncodeException

Unable to encode ( ) ...

Error message

Unable to encode {bodyType} ({headers}) ...

What it means

Feign's MultiEncoder iterates its registered PredicatedEncoders and throws this EncodeException when none of them declares it can encode the request body for the given bodyType. The message includes the body type and template headers, indicating the encoder chain lacks coverage for this request shape. It is thrown before any HTTP call is made.

Solutions

  1. Register a matching encoder, e.g. Feign.builder().encoder(new JacksonEncoder()) or add it to the MultiEncoder.Builder chain
  2. Add a fallback encoder (e.g. new Encoder.Default() which handles String/byte[]) as the last entry
  3. Check the body type in the message and confirm it is the type your interface method actually sends; align the method signature or encoder predicate
  4. Verify the relevant codec module (feign-jackson, feign-gson, etc.) is on the classpath

Example fix

// before
Feign.builder().decoder(new JacksonDecoder()).target(Api.class, url); // POST fails
// after
Feign.builder()
    .encoder(new MultiEncoder.Builder().add(new JacksonEncoder()).add(new Encoder.Default()).build())
    .decoder(new JacksonDecoder())
    .target(Api.class, url);
Defensive patterns

Strategy: validation

Validate before calling

// verify every @Body/@Param object type in the target interface has an encoder
for (Method m : apiInterface.getMethods()) {
  Class<?> bodyType = bodyParamType(m);
  if (bodyType != null && !encoders.stream().anyMatch(e -> e.canEncode(bodyType.newInstance(), bodyType, new RequestTemplate()))) {
    throw new ConfigurationException("No encoder registered for " + bodyType);
  }
}

Try / catch

try {
  return api.post(payload);
} catch (EncodeException e) {
  if (e.getMessage().startsWith("Unable to encode")) {
    throw new ClientConfigurationException("No encoder for body type: " + e.getMessage(), e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Sending a request with a body (e.g. a @Body or @Param object) whose Java type (e.g. a POJO without a JSON encoder registered, or a Map with form encoding expected) matches no registered encoder's canEncode predicate in the MultiEncoder chain.

Common situations: Building a client with only a decoder but no encoder, then POSTing an object; forgetting to register JacksonEncoder/GsonEncoder; sending a raw String/byte[] when the registered encoder only handles JSON types; switching an endpoint from form params to a JSON body without updating encoders.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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

Appendix: source

Thrown at core/src/main/java/feign/codec/MultiEncoder.java:109

  /**
   * Encodes using the first encoder that accepts the request.
   *
   * @param object {@inheritDoc}
   * @param bodyType {@inheritDoc}
   * @param template {@inheritDoc}
   * @throws EncodeException when no encoder accepts the request, or the chosen one fails
   */
  @Override
  public void encode(Object object, Type bodyType, RequestTemplate template)
      throws EncodeException {
    for (PredicatedEncoder encoder : encoders) {
      if (encoder.canEncode(object, bodyType, template)) {
        encoder.encode(object, bodyType, template);
        return;
      }
    }
    throw new EncodeException(unableToEncode(bodyType, template));
  }

  private String unableToEncode(Type bodyType, RequestTemplate template) {
    StringBuilder message =
        new StringBuilder("Unable to encode ")
            .append(bodyType == null ? "request body" : bodyType.getTypeName())
            .append(" (")
            .append(headers(template))
            .append(')');
    if (template.method() != null) {
      message.append(" for ").append(template.method()).append(' ').append(template.path());
    }
    message.append(". Encoders tried, in order:");
    appendTo(message, "\n  ");
    return message
        .append("\nRegister an encoder that accepts it, or add a catch-all")
        .append(" (EncoderPredicate.any()) last.")
        .toString();

View on GitHub (pinned to e2a1e27560)