{"record":{"id":"35024eaf23b8e0d7","repo":"eclipse-vertx/vert.x","slug":"unexpected-trailing-token-35024e","errorCode":null,"errorMessage":"Unexpected trailing token","messagePattern":"Unexpected trailing token","errorType":"exception","errorClass":"DecodeException","httpStatus":null,"severity":"error","filePath":"vertx-core/src/main/java/io/vertx/core/json/jackson/JacksonCodec.java","lineNumber":280,"sourceCode":"\n  public Object fromBuffer(Buffer buf) throws DecodeException {\n    return fromParser(createParser(buf), Object.class);\n  }\n\n  public static <T> T fromParser(JsonParser parser, Class<T> type) throws DecodeException {\n    Object res;\n    JsonToken remaining;\n    try {\n      parser.nextToken();\n      res = parseValue(parser);\n      remaining = parser.nextToken();\n    } catch (IOException e) {\n      throw new DecodeException(e.getMessage(), e);\n    } finally {\n      close(parser);\n    }\n    if (remaining != null) {\n      throw new DecodeException(\"Unexpected trailing token\");\n    }\n    return cast(res, type);\n  }\n\n  /**\n   * Parse a JSON value given the {@code parser}, consuming the current parser token and possibly more\n   * when parsing an object or an array.\n   *\n   * @param parser the parser\n   * @return the parsed value as an object\n   */\n  public static Object parseValue(JsonParser parser) throws IOException, DecodeException {\n    switch (parser.currentTokenId()) {\n      case JsonTokenId.ID_START_OBJECT:\n        return internalParseObject(parser);\n      case JsonTokenId.ID_START_ARRAY:\n        return internalParseArray(parser);\n      case JsonTokenId.ID_STRING:","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/eclipse-vertx/vert.x/blob/fb308bd8c3f12c79f4ae89bef67fadf6c80d036e/vertx-core/src/main/java/io/vertx/core/json/jackson/JacksonCodec.java#L262-L298","documentation":"After parsing the requested JSON value, JacksonCodec checks that the parser has been fully consumed (no remaining token). If extra content follows the first complete JSON value, it throws DecodeException(\"Unexpected trailing token\"). This enforces that the input is exactly one JSON document.","triggerScenarios":"Calling fromString/fromBuffer/fromStream/fromReader with input that contains a valid JSON value followed by extra data — e.g. 'null null', '{...} garbage', NDJSON/multiple concatenated JSON documents, or an HTTP body with appended bytes.","commonSituations":"Decoding newline-delimited JSON (NDJSON) streams a document at a time but passing the whole payload; concatenating JSON responses without an array wrapper; binary padding or a stray BOM/whitespace artifact after the JSON; proxy pipelines that append data.","solutions":["Remove or split the trailing data — parse each JSON document separately (split on newline for NDJSON)","Validate the payload contains exactly one JSON document before decoding","If multiple documents are expected, parse them individually in a loop with a streaming parser","Trim whitespace/BOM; if the extra token is unexpected, log the raw tail and fix the producer"],"exampleFix":"// before\nObject o = Json.decodeValue(ndjsonBuffer); // '{...}\\n{...}' throws\n// after\nfor (String line : ndjsonBuffer.toString().split(\"\\n\")) {\n  if (line.isBlank()) continue;\n  Object o = Json.decodeValue(line);\n  handle(o);\n}","handlingStrategy":"validation","validationCode":"// Java\n// Ensure exactly one JSON document per decode unit\npublic static List<String> splitJsonDocuments(String payload) {\n  return java.util.Arrays.stream(payload.split(\"\\n\"))\n      .map(String::strip)\n      .filter(s -> !s.isEmpty())\n      .toList();\n}","typeGuard":"public static boolean isSingleJsonDocument(String s) {\n  if (s == null) return false;\n  String t = s.strip();\n  if (t.isEmpty()) return false;\n  try (com.fasterxml.jackson.core.JsonParser p = JacksonCodec.createParser(t)) {\n    while (p.nextToken() != null) { /* consume */ }\n    return true;\n  } catch (Exception e) { return false; }\n}","tryCatchPattern":"try {\n  return Json.decodeValue(buffer);\n} catch (DecodeException e) {\n  if (e.getMessage().contains(\"Unexpected trailing token\")) {\n    // input has multiple documents: split and decode each\n    return splitJsonDocuments(buffer.toString()).stream()\n        .map(Json::decodeValue).toList();\n  }\n  throw e;\n}","preventionTips":["Never concatenate JSON documents; wrap multiple values in an array","Handle NDJSON by splitting on newlines and decoding per line","Trim BOM and trailing whitespace before decoding","Agree on single-document payloads with upstream producers"],"tags":["json","decoding","trailing-token","ndjson"],"backgroundTag":"json-parse-error","analyzedSha":"fb308bd8c3f12c79f4ae89bef67fadf6c80d036e","analyzedAt":"2026-09-06T11:37:12.241Z","contentChangedAt":"2026-09-06T11:37:12.241Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}