eclipse-vertx/vert.x · error · DecodeException

Failed to decode:

Error message

Failed to decode:

What it means

DatabindCodec.fromParser delegates JSON parsing to the Jackson ObjectMapper (mapper.readValue(parser, type)). Any exception thrown during databinding (malformed JSON, type coercion failure, wrapped custom exceptions) is caught and rethrown as a Vert.x DecodeException with message 'Failed to decode:' plus the underlying cause message. The original exception is preserved as the cause.

Source

Thrown at vertx-core/src/main/java21/io/vertx/core/json/jackson/v3/DatabindCodec.java:109

    return fromParser(mapper.reader().without(StreamReadFeature.AUTO_CLOSE_SOURCE).createParser(in), clazz);
  }

  public static JsonParser createParser(BufferInternal buf) {
    return DatabindCodec.mapper.createParser((InputStream) new ByteBufInputStream(buf.getByteBuf()));
  }

  public static JsonParser createParser(String str) {
    return DatabindCodec.mapper.createParser(str);
  }

  public static <T> T fromParser(JsonParser parser, Class<T> type) throws DecodeException {
    T value;
    JsonToken remaining;
    try {
      value = DatabindCodec.mapper.readValue(parser, type);
      remaining = parser.nextToken();
    } catch (Exception e) {
      throw new DecodeException("Failed to decode:" + e.getMessage(), e);
    } finally {
      close(parser);
    }
    if (remaining != null) {
      throw new DecodeException("Unexpected trailing token");
    }
    if (type == Object.class) {
      value = (T) adapt(value);
    }
    return value;
  }

  private static <T> T fromParser(JsonParser parser, TypeReference<T> type) throws DecodeException {
    T value;
    try {
      value = DatabindCodec.mapper.readValue(parser, type);
    } catch (Exception e) {
      throw new DecodeException("Failed to decode:" + e.getMessage(), e);

View on GitHub (pinned to fb308bd8c3)

Solutions

  1. Inspect e.getCause() (the wrapped Jackson exception) — it pinpoints the parse/coercion problem and its JSON location.
  2. Validate that the payload is complete, non-empty, and well-formed JSON before decoding.
  3. Check the target type matches the JSON shape (object vs array vs primitive); use the correct Class/TypeReference.
  4. If a custom deserializer threw, fix the data or that deserializer (e.g. invalid Base64).

Example fix

// before
MyType v = Json.decodeValue(body, MyType.class); // throws on bad payload
// after
try {
  MyType v = Json.decodeValue(body, MyType.class);
} catch (DecodeException e) {
  logger.warn("Bad payload: {}", e.getCause() != null ? e.getCause().getMessage() : e.getMessage());
}
Defensive patterns

Strategy: try-catch

Validate before calling

boolean looksLikeJson(String s) {
  String t = s == null ? "" : s.trim();
  return !t.isEmpty() && ((t.charAt(0) == '{' && t.endsWith("}")) || (t.charAt(0) == '[' && t.endsWith("]")));
}

Try / catch

try {
  MyType v = Json.decodeValue(payload, MyType.class);
} catch (DecodeException e) {
  Throwable cause = e.getCause();
  log.error("Decode failed: {}", cause != null ? cause.getMessage() : e.getMessage());
  return Result.badRequest("malformed payload");
}

Prevention

When it happens

Trigger: Calling Json.decodeValue(buffer/string, Class) or DatabindCodec.fromValue/fromParser with input that Jackson cannot bind: malformed JSON syntax, wrong target type (e.g. decoding a JSON array into a POJO), or a deserializer that throws (like the base64 error).

Common situations: Decoding an empty or truncated payload from an HTTP body or Kafka message; a service contract change where the sender emits arrays but the receiver expects an object; decoding '{"n":1}x' with trailing garbage (Jackson fails or the trailing-token check fires).

Understand the failure class

Related errors


AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06). Data as JSON: /api/errors/3164a04c7d53d1df. Report an issue: GitHub.