apple/pkl · error · ParserError
unexpectedEndOfFile
unexpectedEndOfFile
Error message
Unexpected end of file.
What it means
The Pkl parser reached the end of the input file while it still expected more tokens — for example in the middle of an expression, argument list, or identifier reference. The parser throws this instead of a generic unexpected-token error when the lookahead token is EOF, because the real problem is missing input, not wrong input. It points at the position one past the last parsed token.
Source
Thrown at pkl-parser/src/main/java/org/pkl/parser/ParserImpl.java:1040
var tk = next();
yield new FloatLiteralExpr(tk.text(lexer), tk.span);
}
case STRING_START -> parseSingleLineStringLiteralExpr();
case STRING_MULTI_START -> parseMultiLineStringLiteralExpr();
case IDENTIFIER -> {
var identifier = parseIdentifier();
if (lookahead == Token.LPAREN
&& !precededBySemicolon
&& _lookahead.newLinesBetween == 0) {
var args = parseArgumentList();
yield new UnqualifiedAccessExpr(
identifier, args, identifier.span().endWith(args.span()));
} else {
yield new UnqualifiedAccessExpr(identifier, null, identifier.span());
}
}
case EOF ->
throw new ParserError(
ErrorMessages.create("unexpectedEndOfFile"), prev.span.stopSpan().move(1));
default -> {
var text = _lookahead.text(lexer);
if (expectation != null) {
throw parserError("unexpectedToken", text, expectation);
}
throw parserError("unexpectedTokenForExpression", text);
}
};
return parseExprRest(expr);
}
@SuppressWarnings("DuplicatedCode")
private Expr parseExprRest(Expr expr) {
// non-null
if (lookahead == Token.NON_NULL) {
var end = next().span;
var res = new NonNullExpr(expr, expr.span().endWith(end));View on GitHub (pinned to f3efcbfc9b)
Solutions
- Scan the source at the reported stop position and close the nearest unclosed bracket, paren, or quote above it
- Check for a trailing incomplete expression (e.g. a dangling operator or arrow) and complete or remove it
- Verify the file was fully written/transferred — compare byte size or re-save the source
- If generating Pkl programmatically, assert balanced delimiters before invoking the parser
Example fix
// before (truncated) x = foo( // after x = foo(1, 2)
Defensive patterns
Strategy: try-catch
Validate before calling
function checkBalancedDelimiters(src) {
const pairs = {'{':'}','(':')','[':']'};
const stack = [];
let inStr = false, esc = false;
for (const ch of src) {
if (esc) { esc = false; continue; }
if (ch === '\\') { esc = true; continue; }
if (ch === '"') { inStr = !inStr; continue; }
if (inStr) continue;
if (pairs[ch]) stack.push(pairs[ch]);
else if (Object.values(pairs).includes(ch) && stack.pop() !== ch) return false;
}
return !inStr && stack.length === 0;
}
// run before invoking the parser; abort if false Type guard
null
Try / catch
try {
const result = pkl.evaluateSource(src);
} catch (e) {
if (e instanceof ParserError && e.errorId === 'unexpectedEndOfFile') {
// append missing closers or report position e.span.start
} else { throw e; }
} Prevention
- Lint source for balanced brackets/quotes before parsing
- Never truncate generated Pkl output — write files atomically
- Use an editor with Pkl syntax highlighting to catch unclosed delimiters early
When it happens
Trigger: Calling the parser (e.g. via pkl.evaluateSource / ParserImpl.parse) on source that ends prematurely: an unclosed `{`, `(`, `[`, or string literal, a trailing operator like `1 +`, a dangling `->` in a type, or a truncated file passed in.
Common situations: Files cut off by an incomplete download or failed save; copy-pasting a snippet and dropping the closing brace; generating Pkl code programmatically and emitting an unbalanced delimiter; heredoc/template tooling swallowing the final lines.
Related errors
- missingDelimiter
- stringContentMustBeginOnNewLine
- notAUnion
- closingStringDelimiterMustBeginOnNewLine
- ErrorMessages.create(errorKey, args)
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/d01a138bc4dc2126.
Report an issue: GitHub.