apple/pkl · error

Unexpected character

Error message

Unexpected character `{0}`. Did you mean `{1}`?

What it means

The lexer's nextDefault() sees a lone `&` character, which is not a valid Pkl token by itself. Pkl only defines the `&&` logical-and operator, so the lexer suggests `&&` via unexpectedChar(). This is a lexical scan error raised before any parsing occurs.

Solutions

  1. Replace `&` with `&&` if a logical-and was intended.
  2. Remove the stray `&` if it is a typo.
  3. Move the character into a string literal or comment if it was meant as data.

Example fix

// before
if (a & b) ...

// after
if (a && b) ...
Defensive patterns

Strategy: validation

Validate before calling

function assertNoSingleAmpersand(src) {
  const bad = src.replace(/&&/g, "").match(/&/);
  if (bad) throw new Error(`Lone '&' at index ${bad.index}; use '&&'`);
}

Prevention

When it happens

Trigger: Writing a single `&` in Pkl source — e.g. a bitwise-and habit from other languages, a typo writing `&&` as `&`, or an ampersand inside code that wasn't quoted or commented.

Common situations: Translating Java/C/TypeScript expressions to Pkl; shell scripts or YAML with unescaped `&` pasted into Pkl; template escaping mistakes.

Related errors


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

Appendix: source

Thrown at pkl-parser/src/main/java/org/pkl/parser/Lexer.java:197

          nextChar();
          yield Token.NOT_EQUAL;
        } else yield Token.NOT;
      }
      case '?' -> {
        if (lookahead == '.') {
          nextChar();
          yield Token.QDOT;
        } else if (lookahead == '?') {
          nextChar();
          yield Token.COALESCE;
        } else yield Token.QUESTION;
      }
      case '&' -> {
        if (lookahead == '&') {
          nextChar();
          yield Token.AND;
        } else {
          throw unexpectedChar(ch, "&&");
        }
      }
      case '|' -> {
        if (lookahead == '>') {
          nextChar();
          yield Token.PIPE;
        } else if (lookahead == '|') {
          nextChar();
          yield Token.OR;
        } else {
          yield Token.UNION;
        }
      }
      case '*' -> {
        if (lookahead == '*') {
          nextChar();
          yield Token.POW;
        } else yield Token.STAR;

View on GitHub (pinned to f3efcbfc9b)