apple/pkl · error · VmException

forWhenBodyCannotHaveParameters

forWhenBodyCannotHaveParameters

Error message

forWhenBodyCannotHaveParameters

What it means

In `for`/`when` generator expressions, the body object cannot declare parameters — the iteration variables are already bound by the `for` clause. If the body's parameter list is non-empty, doVisitForWhenBody throws forWhenBodyCannotHaveParameters, pointing at the first parameter.

Source

Thrown at pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java:1548

  @Override
  public GeneratorMemberNode visitWhenGenerator(WhenGenerator member) {
    var sourceSection = createSourceSection(member);
    var thenNodes = doVisitForWhenBody(member.getThenClause());
    var elseNodes =
        member.getElseClause() == null
            ? new GeneratorMemberNode[0]
            : doVisitForWhenBody(member.getElseClause());

    // when predicates cannot see their direct scope
    var predicateNode =
        symbolTable.enterEagerGenerator((scope) -> visitExpr(member.getPredicate()));
    return new GeneratorWhenNode(sourceSection, predicateNode, thenNodes, elseNodes);
  }

  private GeneratorMemberNode[] doVisitForWhenBody(ObjectBody body) {
    if (!body.getParameters().isEmpty()) {
      throw exceptionBuilder()
          .evalError("forWhenBodyCannotHaveParameters")
          .withSourceSection(createSourceSection(body.getParameters().get(0)))
          .build();
    }
    return doVisitGeneratorMemberNodes(body.getMembers());
  }

  private @Nullable FrameSlotVariable makeBinding(
      org.pkl.parser.syntax.@Nullable Parameter parameter) {
    if (!(parameter instanceof TypedIdentifier typedIdentifier)) {
      return null;
    }
    var name = typedIdentifier.getIdentifier().getValue();
    return symbolTable
        .getCurrentScope()
        .frameDescriptorBuilder
        .addSlot(FrameSlotKind.Illegal, toIdentifier(name), null);
  }

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Remove the parameter list from the when body object.
  2. Access needed values through the for-bound variables or captures instead of parameters.
  3. If parameterization is required, use a plain function or a different construct than for/when generators.

Example fix

// before
for (x in xs) when (x > 0) (y) { emit(x) }
// after
for (x in xs) when (x > 0) { emit(x) }
Defensive patterns

Strategy: validation

Validate before calling

// Ensure generator when-bodies have no parameter list before rendering:
if (body.params && body.params.length > 0) throw new Error('for/when bodies cannot take parameters');

Prevention

When it happens

Trigger: Writing `for (x in xs) when (cond) (y) { ... }` — supplying a parameter list on the body of a when-branch within a for-generator member.

Common situations: Confusing generator `when` bodies with class or function bodies that take parameters, or leftover syntax from refactoring a regular member into a generator.

Related errors


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