apple/pkl · error · ParserError

notAUnion

notAUnion

Error message

Only type unions can have a default marker (*).

What it means

Pkl reserves the `*` prefix on types to mark the default member of a type union. The parser collects default markers while parsing a type list, and if it saw one (defaultIndex == 0 means a marker appeared) but the `|` union token never followed — so the result isn't a union — it rejects the marker with notAUnion, spanning from the type's start through the first type.

Source

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

  private Type parseType() {
    return parseType(null);
  }

  private Type parseType(@Nullable String expectation) {
    var defaultIndex = -1;
    Span start = null;
    if (lookahead == Token.STAR) {
      defaultIndex = 0;
      start = next().span;
    }
    var first = parseTypeAtom(expectation);
    if (start == null) {
      start = first.span();
    }

    if (lookahead != Token.UNION) {
      if (defaultIndex == 0) {
        throw new ParserError(ErrorMessages.create("notAUnion"), start.endWith(first.span()));
      }
      return first;
    }

    var types = new ArrayList<Type>();
    types.add(first);
    var end = start;
    var i = 1;
    while (lookahead == Token.UNION) {
      next();
      if (lookahead == Token.STAR) {
        if (defaultIndex != -1) {
          throw parserError("multipleUnionDefaults");
        }
        defaultIndex = i;
        next();
      }
      var type = parseTypeAtom(expectation);

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Either remove the `*` if a plain single type was intended (`String` instead of `*String`)
  2. Or complete the union: `*String | Int` — the default marker is only valid inside a `|` union
  3. Check the parser/emitter that generated the type expression for a dropped `|` token

Example fix

// before
x: *String
// after
x: *String | Int  // or just: x: String
Defensive patterns

Strategy: validation

Validate before calling

function hasStrayDefaultMarker(typeExpr) {
  // '*' is only valid immediately before a member of a '|' union
  return /(^|[\s,&(])\*[^|]*$/.test(typeExpr) && !typeExpr.includes('|');
}
// flag type expressions containing '*' but no '|' before parsing

Type guard

null

Try / catch

try {
  const result = parser.parseType(source);
} catch (e) {
  if (e.errorId === 'notAUnion') {
    // strip the '*' or complete the union with '|'
  } else { throw e; }
}

Prevention

When it happens

Trigger: Writing `*String` or `foo: *Int` where no union `|` follows — the `*` is applied to a lone type; also `*String?` in an amortized/`*T&...` conjunction context where no UNION token is produced.

Common situations: Confusing Pkl's union-default `*A|B` syntax with other languages' pointer/deref or splat `*` prefixes; a typo dropping the `| B` half of a union; old snippets from pre-union Pkl drafts.

Related errors


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