{"record":{"id":"5101ad15fc85538e","repo":"apple/pkl","slug":"unexpected-character","errorCode":null,"errorMessage":"Unexpected character","messagePattern":"Unexpected character","errorType":"exception","errorClass":"ParseException","httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/util/json/JsonParser.java","lineNumber":126,"sourceCode":"  public void parse(Reader reader, int buffersize) throws IOException {\n    if (buffersize <= 0) {\n      throw new IllegalArgumentException(\"buffersize is zero or negative\");\n    }\n    this.reader = reader;\n    buffer = new char[buffersize];\n    bufferOffset = 0;\n    index = 0;\n    fill = 0;\n    line = 1;\n    lineOffset = 0;\n    current = 0;\n    captureStart = -1;\n    read();\n    skipWhiteSpace();\n    readValue();\n    skipWhiteSpace();\n    if (!isEndOfText()) {\n      throw error(\"Unexpected character\");\n    }\n  }\n\n  private void readValue() throws IOException {\n    switch (current) {\n      case 'n' -> readNull();\n      case 't' -> readTrue();\n      case 'f' -> readFalse();\n      case '\"' -> readString();\n      case '[' -> readArray();\n      case '{' -> readObject();\n      case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> readNumber();\n      default -> throw expected(\"value\");\n    }\n  }\n\n  private void readArray() throws IOException {\n    var array = handler.startArray();","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/util/json/JsonParser.java#L108-L144","documentation":"Pkl's internal JSON parser throws 'Unexpected character' when, after parsing the top-level JSON value and skipping whitespace, there are additional non-whitespace characters left in the input. Valid JSON must contain exactly one value; trailing garbage (another value, stray brace, commentary) is rejected. The error surfaces to users as a jsonParseError hint from `json.parse`.","triggerScenarios":"Calling `json.parse` on input like `{\"a\":1}{\"b\":2}`, `123 abc`, `\"x\" \"y\"`, or JSON followed by a newline-containing log entry. Raised in JsonParser.parse when !isEndOfText() after readValue().","commonSituations":"Parsing JSON Lines / NDJSON files (multiple values, one per line) with a single-value parser; concatenating JSON files; a log file where JSON is followed by plain text lines.","solutions":["Parse NDJSON/JSON Lines by splitting on newlines and calling json.parse per line","Remove trailing characters after the single JSON value in the input","Ensure you are not concatenating multiple JSON documents into one string","Extract just the first JSON document if the source emits one value plus extra output"],"exampleFix":"// before\nobj = json.parse(fileText)  // NDJSON file\n// after\nobjs = fileText.split(\"\\n\").filter((l) -> l.trim() != \"\").map((l) -> json.parse(l))","handlingStrategy":"validation","validationCode":"// NDJSON detection: more than one top-level value\nlines = text.split(\"\\n\").filter((l) -> l.trim() != \"\")\nassert(lines.length == 1, \"use per-line parsing for NDJSON\")","typeGuard":null,"tryCatchPattern":"try {\n  value = json.parse(text)\n} catch (e) {\n  // possibly multiple documents -> parse line by line\n  values = text.split(\"\\n\").filter((l) -> l.trim() != \"\").map((l) -> json.parse(l))\n}","preventionTips":["Detect NDJSON/JSON Lines inputs and parse per line","Trim or slice input to exactly one JSON value","Never concatenate JSON files; parse them separately"],"tags":["json","pkl","parser","trailing-content"],"backgroundTag":"json-parse-error","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}