apple/pkl · error · GenericParserError

keywordNotAllowedHere

keywordNotAllowedHere

Error message

Keyword `{0}` is not allowed here.

What it means

The parser throws keywordNotAllowedHere (message 'Keyword `{0}` is not allowed here.') when a Pkl keyword token appears where a module member should start. Only identifiers and the specific keywords class/typealias/function may begin a module member; any other keyword (e.g. `if`, `let`, `when`, `is`) in top-level member position is rejected, with the offending keyword text as the parameter.

Source

Thrown at pkl-parser/src/main/java/org/pkl/parser/GenericParserImpl.java:204

    while (lookahead.isModifier()) {
      modifiers.add(make(NodeType.MODIFIER, next().span));
      hasModifier = true;
      ff(children);
    }
    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()));

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Remove the stray keyword or replace it with a valid module member declaration (property, class, typealias, or method).
  2. If the keyword was meant as an identifier, rename it to a non-reserved name (e.g. `when` -> `whenValue`).
  3. Check the surrounding lines for a deleted/merged member definition that left the keyword orphaned.

Example fix

// before
module my.mod
if (cond) { x = 1 }
// after
module my.mod
x = if (cond) 1 else 2
Defensive patterns

Strategy: validation

Validate before calling

// lint: first token of each top-level line must not be a non-member keyword
const memberKeywords = new Set(["class", "typealias", "function"]);
const reserved = new Set(["if", "let", "when", "is", "amends", "extends", "import", "module", "nothing", "unknown", "read", "output", "super", "this", "outer"]);
for (const line of pklSource.split("\n")) {
  const w = line.trim().split(/\s+/)[0];
  if (reserved.has(w) && !memberKeywords.has(w)) throw new Error(`Keyword ${w} not allowed at member position`);
}

Prevention

When it happens

Trigger: Writing a bare keyword at top level of a .pkl module, e.g. `if` or `let` as the first token of a line in the module body, or accidentally deleting a member name so only its keyword remains (e.g. a stray `function` without a name is handled, but `is SomeType` at top level triggers this).

Common situations: Copy-pasting expression-level syntax into module scope; a botched find/replace removing an identifier and leaving its keyword; typos where a reserved word was meant to be an identifier (e.g. using `when` as a property name unquoted).

Related errors


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