apple/pkl · error · ParseException
Unexpected character
Error message
Unexpected character
What it means
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`.
Source
Thrown at pkl-core/src/main/java/org/pkl/core/util/json/JsonParser.java:126
public void parse(Reader reader, int buffersize) throws IOException {
if (buffersize <= 0) {
throw new IllegalArgumentException("buffersize is zero or negative");
}
this.reader = reader;
buffer = new char[buffersize];
bufferOffset = 0;
index = 0;
fill = 0;
line = 1;
lineOffset = 0;
current = 0;
captureStart = -1;
read();
skipWhiteSpace();
readValue();
skipWhiteSpace();
if (!isEndOfText()) {
throw error("Unexpected character");
}
}
private void readValue() throws IOException {
switch (current) {
case 'n' -> readNull();
case 't' -> readTrue();
case 'f' -> readFalse();
case '"' -> readString();
case '[' -> readArray();
case '{' -> readObject();
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> readNumber();
default -> throw expected("value");
}
}
private void readArray() throws IOException {
var array = handler.startArray();View on GitHub (pinned to f3efcbfc9b)
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
Example fix
// before
obj = json.parse(fileText) // NDJSON file
// after
objs = fileText.split("\n").filter((l) -> l.trim() != "").map((l) -> json.parse(l)) Defensive patterns
Strategy: validation
Validate before calling
// NDJSON detection: more than one top-level value
lines = text.split("\n").filter((l) -> l.trim() != "")
assert(lines.length == 1, "use per-line parsing for NDJSON") Try / catch
try {
value = json.parse(text)
} catch (e) {
// possibly multiple documents -> parse line by line
values = text.split("\n").filter((l) -> l.trim() != "").map((l) -> json.parse(l))
} Prevention
- 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
When it happens
Trigger: 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().
Common situations: 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.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/5101ad15fc85538e.
Report an issue: GitHub.