eclipse-vertx/vert.x · error · DecodeException

Unexpected trailing token

Error message

Unexpected trailing token

What it means

After Jackson successfully binds a value in DatabindCodec.fromParser, the codec calls parser.nextToken() to confirm the stream ends. If another token remains (extra JSON content after the first value), it throws DecodeException('Unexpected trailing token'). This enforces that input contains exactly one JSON value with nothing after it.

Source

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

  }

  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);
    } finally {
      close(parser);
    }
    if (type.getType() == Object.class) {
      value = (T) adapt(value);

View on GitHub (pinned to fb308bd8c3)

Solutions

  1. Split the input and decode one JSON document at a time (e.g. split on lines for NDJSON and call decodeValue per line).
  2. Trim/sanitize the payload so only one JSON value remains.
  3. Fix the producer that is concatenating JSON documents.
  4. If multi-document input is expected, use Jackson's MappingIterator via mapper.readValues(parser, type) instead of single-value decode.

Example fix

// before
JsonObject o = Json.decodeValue(ndjsonBuffer, JsonObject.class); // two lines -> fails
// after
JsonObject o = Json.decodeValue(ndjsonBuffer.toString().split("\n")[0], JsonObject.class);
Defensive patterns

Strategy: validation

Validate before calling

boolean singleJsonDocument(String s) {
  String t = s.trim();
  // quick sanity: first non-space char opens, last closes, and lengths plausibly match one doc
  return t.startsWith("{") || t.startsWith("[") || t.startsWith("\"") || t.matches("^-?\\d.*") || t.equals("true") || t.equals("false") || t.equals("null");
}

Try / catch

try {
  JsonObject o = Json.decodeValue(buf, JsonObject.class);
} catch (DecodeException e) {
  if (e.getMessage().contains("trailing token")) {
    // split and decode each document separately
  }
}

Prevention

When it happens

Trigger: Json.decodeValue on input containing concatenated values like '{}{}' or '[1,2] extra', or newline-delimited JSON (NDJSON) fed to a single-value decoder; whitespace is fine, extra tokens are not.

Common situations: NDJSON logs streamed into Json.decodeValue instead of a line-by-line parser; a producer accidentally emitting the same object twice; concatenating buffered HTTP chunks that each hold a JSON document.

Related errors


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