apple/pkl · error · ParseException
hexadecimal digit
Error message
hexadecimal digit
What it means
A \u escape in a JSON string must be followed by exactly four hexadecimal digits. readEscape reads 4 characters after \u and calls isHexDigit on each; the first non-hex character (or EOF) triggers expected("hexadecimal digit"). Common with truncated \u escapes or typos like \u12g4.
Solutions
- Complete the escape with exactly 4 hex digits, e.g. \u00e9 instead of \ue or \u12.
- If the character is basic ASCII, drop the escape entirely and write the character directly.
- Check for template interpolation colliding with the escape sequence and re-order interpolation/escaping.
- Catch ParseException and use the reported position to inspect the malformed \u sequence.
Example fix
// before
String json = "{\"ch\": \"\\u12\"}"; // incomplete escape
// after
String json = "{\"ch\": \"\\u0012\"}"; Defensive patterns
Strategy: validation
Validate before calling
// Java: find malformed \u escapes before parsing
if (java.util.regex.Pattern.compile("\\\\u(?![0-9a-fA-F]{4})").matcher(json).find()) {
throw new IllegalArgumentException("\\u escape must be followed by exactly 4 hex digits");
} Try / catch
try {
parser.parse(json);
} catch (ParseException e) {
if (e.getMessage().contains("hexadecimal digit")) {
throw new IllegalArgumentException("Malformed \\uXXXX escape at " + e.getLocation().line + ":" + e.getLocation().column, e);
}
throw e;
} Prevention
- Always write 4 hex digits after \u (pad with zeros).
- Check template interpolation isn't splitting escape sequences.
- Prefer writing the literal character instead of manual \u escapes.
- Regex-audit generated files for \u followed by fewer than 4 hex digits.
When it happens
Trigger: parse() on strings containing "\u12", "\uABCG", "\u{" (template variable eaten by the escape), or a string boundary cutting the escape short.
Common situations: Hand-escaped unicode in config files, templating engines interpolating into the middle of a \u escape, truncated transfer of JSON over a size-limited channel, regex/string processing that mangled escape sequences.
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/eab3675d2675ea16.
Report an issue: GitHub.
Appendix: source
Thrown at pkl-core/src/main/java/org/pkl/core/util/json/JsonParser.java:282
read();
return string;
}
private void readEscape() throws IOException {
read();
switch (current) {
case '"', '/', '\\' -> captureBuffer.append((char) current);
case 'b' -> captureBuffer.append('\b');
case 'f' -> captureBuffer.append('\f');
case 'n' -> captureBuffer.append('\n');
case 'r' -> captureBuffer.append('\r');
case 't' -> captureBuffer.append('\t');
case 'u' -> {
var hexChars = new char[4];
for (var i = 0; i < 4; i++) {
read();
if (!isHexDigit()) {
throw expected("hexadecimal digit");
}
hexChars[i] = (char) current;
}
captureBuffer.append((char) Integer.parseInt(new String(hexChars), 16));
}
default -> throw expected("valid escape sequence");
}
read();
}
private void readNumber() throws IOException {
handler.startNumber();
startCapture();
readChar('-');
var firstDigit = current;
if (!readDigit()) {
throw expected("digit");
}View on GitHub (pinned to f3efcbfc9b)