apple/pkl · error · ParserError

invalidSeparatorPosition

invalidSeparatorPosition

Error message

Unexpected separator character.

The separator character (`_`) cannot follow `0x`, `0b`, `.`, `e`, or 'E' in a number literal.

What it means

In numeric literals, `_` is a digit separator and may only appear between digits. The lexer rejects a separator in positions where no digit precedes it — specifically immediately after `0x`/`0b` prefixes, after a leading `.`, or after the `e`/`E` exponent marker — because `0x_`, `._`, or `1e_` are not valid numbers.

Solutions

  1. Move the separator so it sits between digits: `0xFF` → `0xF_F` is fine, `0x_FF` is not.
  2. Delete the stray `_`: `1e_5` → `1e5`, `1._5` → `1.5`.
  3. For hex/binary, put the first separator only after at least one digit: `0xFF_FF`.
  4. For floats, keep separators only within digit runs, never adjacent to `.`, `e`, or `E`: `1_000.000_1e10`.

Example fix

// before
mask = 0x_FF00
// after
mask = 0xFF_00
Defensive patterns

Strategy: validation

Validate before calling

function badSeparator(src) { return /0[xX]_|0[bB]_|\._|\d[eE]_/.test(src); }
if (badSeparator(src)) throw new Error('Digit separator _ cannot follow 0x, 0b, ., e, or E');

Type guard

null

Try / catch

try { tokens = lexer.next(); } catch (e) { if (e.code === 'invalidSeparatorPosition') { console.error('Place _ only between digits'); } throw e; }

Prevention

When it happens

Trigger: lexNumber (via nextDefault): in the fraction branch, after consuming `.` following digits, lookahead is `_` (e.g. `1._5`); equivalent checks reject `0x_FF`, `0b_1`, and `1e_5` forms in the prefix/exponent branches. Any `_` directly following `0x`, `0b`, `.`, `e`, or `E` throws this error.

Common situations: Copy-pasting numbers from languages/specs that allow separators after base prefixes (e.g. Rust `0x_FF`); adding separators for readability in the wrong spot like `1e_6` or `1.5e_3`; typos while formatting large hex or float literals.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

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

        return Token.FLOAT;
      }
    } else if (start == '.') {
      lexDotNumber();
      return Token.FLOAT;
    }

    while ((lookahead >= '0' && lookahead <= '9') || lookahead == '_') {
      nextChar();
    }

    if (lookahead == 'e' || lookahead == 'E') {
      nextChar();
      lexExponent();
      return Token.FLOAT;
    } else if (lookahead == '.') {
      nextChar();
      if (lookahead == '_') {
        throw lexError("invalidSeparatorPosition");
      }
      if (lookahead < '0' || lookahead > '9') {
        backup();
        return Token.INT;
      }
      lexDotNumber();
      return Token.FLOAT;
    }
    return Token.INT;
  }

  private Token lexSlash() {
    switch (lookahead) {
      case '/':
        {
          nextChar();
          var token = lookahead == '/' ? Token.DOC_COMMENT : Token.LINE_COMMENT;
          while (lookahead != '\n' && lookahead != '\r' && lookahead != EOF) {

View on GitHub (pinned to f3efcbfc9b)