apple/pkl · error · ParserError

unexpectedCurlyProbablyAmendsExpression

unexpectedCurlyProbablyAmendsExpression

Error message

Unexpected token: `'{'`.

If you meant to write an amends expression, wrap the parent in parentheses. Try: `({0}) '{' ... '}'`

What it means

A `{` followed an expression that cannot be amended implicitly. Only parenthesized expressions, amends expressions, and `new` expressions can take a `{ ... }` body directly; for anything else the parser throws "Unexpected token: `'{'`" with a hint to wrap the parent in parentheses to write an amends expression.

Solutions

  1. Wrap the base expression in parentheses as the message suggests: `(foo.bar) { ... }`.
  2. If the base is a module-level or imported default, prefer explicit `... { ... }` amends syntax or `new { ... }` for fresh objects.
  3. If `{ ... }` was meant as a block, restructure — Pkl expressions do not take braces except for amends/new.
  4. Check whether you actually wanted object-literal syntax (`new Type { ... }`) rather than amending an arbitrary expression.

Example fix

// before
x = config.bird { name = "tweety" }
// after
x = (config.bird) { name = "tweety" }
Defensive patterns

Strategy: validation

Validate before calling

// Only parenthesized/new/amends bases may take a trailing '{ }'
const BARE_AMEND = /^\s*(?!new\b)(?!\()[A-Za-z_][\w.]*\s*\{/;
function needsParens(line) { return BARE_AMEND.test(line); }
// if (needsParens('config.bird { name = "x" }')) fix by wrapping base in parens

Prevention

When it happens

Trigger: Writing `foo.bar { ... }` intending to amend the result; `myExpr { ... }` where `myExpr` is a plain identifier/qualified access; `if (cond) x { ... }` style trailing bodies; amending the result of a function call without parens: `fetch() { ... }`.

Common situations: Users expecting trailing-`{}` amend sugar to work on any expression; converting `new Foo { ... }` patterns to computed values; qualified accesses to defaults that should be amended via parentheses.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at pkl-parser/src/main/java/org/pkl/parser/ParserImpl.java:1069

  }

  @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));
      return parseExprRest(res);
    }
    // amends
    if (lookahead == Token.LBRACE) {
      if (expr instanceof ParenthesizedExpr
          || expr instanceof AmendsExpr
          || expr instanceof NewExpr) {
        var body = parseObjectBody();
        return parseExprRest(new AmendsExpr(expr, body, expr.span().endWith(body.span())));
      }
      throw parserError("unexpectedCurlyProbablyAmendsExpression", expr.text(lexer.getSource()));
    }
    // qualified access
    if (lookahead == Token.DOT || lookahead == Token.QDOT) {
      var isNullable = next().token == Token.QDOT;
      var identifier = parseIdentifier();
      ArgumentList argumentList = null;
      if (lookahead == Token.LPAREN && !precededBySemicolon && _lookahead.newLinesBetween == 0) {
        argumentList = parseArgumentList();
      }
      var lastSpan = argumentList != null ? argumentList.span() : identifier.span();
      var res =
          new QualifiedAccessExpr(
              expr, identifier, isNullable, argumentList, expr.span().endWith(lastSpan));
      return parseExprRest(res);
    }
    // subscript (needs to be in the same line as the expression)
    if (lookahead == Token.LBRACK && !precededBySemicolon && _lookahead.newLinesBetween == 0) {
      next();

View on GitHub (pinned to f3efcbfc9b)