apple/pkl · error · GenericParserError
invalidTopLevelToken
invalidTopLevelToken
Error message
Invalid token at position. Expected a class, typealias, method, or property.
What it means
The parser throws invalidTopLevelToken ("Invalid token at position. Expected a class, typealias, method, or property.") when a token that is neither a keyword nor a doc comment starts a top-level module member position and is not one of the allowed member starts. parseModuleMember accepts only IDENTIFIER (property), TYPE_ALIAS, CLASS, or FUNCTION; anything else falls through to this catch-all error.
Source
Thrown at pkl-parser/src/main/java/org/pkl/parser/GenericParserImpl.java:209
if (hasModifier) children.add(new Node(NodeType.MODIFIER_LIST, modifiers));
return new HeaderResult(hasDocComment, hasAnnotation, hasModifier);
}
private Node parseModuleMember(List<Node> preChildren) {
return switch (lookahead) {
case IDENTIFIER -> parseClassProperty(preChildren);
case TYPE_ALIAS -> parseTypeAlias(preChildren);
case CLASS -> parseClass(preChildren);
case FUNCTION -> parseClassMethod(preChildren);
case EOF -> throw parserError("unexpectedEndOfFile");
default -> {
if (lookahead.isKeyword()) {
throw parserError("keywordNotAllowedHere", lookahead.text());
}
if (lookahead == Token.DOC_COMMENT) {
throw parserError("danglingDocComment");
}
throw parserError("invalidTopLevelToken");
}
};
}
private Node parseTypeAlias(List<Node> preChildren) {
var headerParts = getHeaderParts(preChildren);
var children = new ArrayList<>(headerParts.preffixes);
var headers = new ArrayList<Node>();
if (headerParts.modifierList != null) {
headers.add(headerParts.modifierList);
}
// typealias keyword
headers.add(makeTerminal(next()));
ff(headers);
headers.add(parseIdentifier());
ff(headers);
if (lookahead == Token.LT) {
headers.add(parseTypeParameterList());View on GitHub (pinned to f3efcbfc9b)
Solutions
- Replace the invalid token with a valid module member: a property (`name = value`), `class X {}`, `typealias Y = ...`, or `function f() = ...`.
- Check for unbalanced braces above the offending line — an earlier mistake can push object-level tokens into top-level position.
- If an annotation is dangling, add the declaration it annotates or remove the annotation.
- If the content belongs inside an object or class body, move it under the appropriate declaration.
Example fix
// before module my.mod "some stray string" // after module my.mod greeting = "some stray string"
Defensive patterns
Strategy: validation
Validate before calling
// lint: top-level member lines must start with an identifier or a member keyword/annotation
const ok = /^\s*([A-Za-z_][\w.]*|class|typealias|function|@|\/\/\/|\/\/|}|$)/;
for (const line of pklSource.split("\n")) {
if (!ok.test(line)) throw new Error(`Invalid top-level token: ${line.trim().slice(0, 20)}`);
} Prevention
- Start every top-level line with a property name, class/typealias/function keyword, or annotation.
- Fix brace balance before assuming the reported line is the real problem.
- Don't paste object-body expressions directly into module scope.
When it happens
Trigger: Placing an expression, literal, operator, punctuation, or string at module top level, e.g. a stray `}`, `= 5`, a quoted string line, a `@Annotation` with no following declaration, or a number literal in the module body.
Common situations: Accidentally pasting object/expression content at module scope; unbalanced braces earlier in the file causing the parser to see body tokens at top level; an annotation left with its target declaration deleted; typos like starting a line with `=` or `.`.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/b6cbe5dc2cfab02e.
Report an issue: GitHub.