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

  1. Replace the invalid token with a valid module member: a property (`name = value`), `class X {}`, `typealias Y = ...`, or `function f() = ...`.
  2. Check for unbalanced braces above the offending line — an earlier mistake can push object-level tokens into top-level position.
  3. If an annotation is dangling, add the declaration it annotates or remove the annotation.
  4. 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

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

Related errors


AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/b6cbe5dc2cfab02e. Report an issue: GitHub.