karatelabs/karate · error · ParserException

invalid numeric separator

Error message

invalid numeric separator

What it means

Numeric separators (_) are allowed only between digits of a numeric literal. In a hex literal, an _ immediately after the 0x prefix (before any digit) is invalid, so the lexer throws 'invalid numeric separator'. Move the separator after at least one hex digit or remove it.

Solutions

  1. Remove the underscore after the prefix: write 0xF0F0 instead of 0x_F0F0
  2. Place separators only between digits: 0xFF_FF is valid
  3. Validate generated literal strings so the digit part is never empty
  4. If the value comes from data, parse it with parseInt(str, 16) instead of embedding it as a literal

Example fix

// before
var mask = 0x_F0F0;
// after
var mask = 0xF0F0;   // or 0xF0_F0 for readability
Defensive patterns

Strategy: validation

Validate before calling

if (/^0[xX]_/.test(literalSrc)) throw new Error('numeric separator cannot follow the 0x prefix');

Type guard

null

Try / catch

try {
  karate.eval('var n = ' + literalSrc + ';');
} catch (e) {
  if (String(e.message).indexOf('invalid numeric separator') >= 0) {
    literalSrc = literalSrc.replace(/^0[xX]_/, '0x'); // strip bad separator and retry
  } else throw e;
}

Prevention

When it happens

Trigger: A JS hex literal evaluated by karate-js starts with 0x_ (underscore directly after the prefix), e.g. var mask = 0x_F0F0 — hit at JsLexer.java:459 in scanNumber.

Common situations: Copy-pasted constants from spec sheets where the prefix was written as 0x_, formatting habit from other tools, template-generated literals where the digit part was empty, typos while aligning bit masks.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/5ec091ff62754d0f. Report an issue: GitHub.

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/parser/JsLexer.java:459

    // ========== Numbers ==========

    private TokenType scanNumber() {
        char c = peek();

        // Hex number
        if (c == '0' && (peek(1) == 'x' || peek(1) == 'X')) {
            advance(); // 0
            advance(); // x
            int hexStart = pos;
            // Fast path: hex digits only, no separator
            while (!isAtEnd() && isHexDigit(peek())) {
                advance();
            }
            // Rare path: separator(s) inside hex literal — must follow a digit, not the prefix
            if (!isAtEnd() && peek() == '_') {
                if (pos == hexStart) {
                    throw new ParserException("invalid numeric separator");
                }
                scanHexDigitsWithSeparators();
            }
            // Rare path: BigInt suffix — `0xff_ffn` is valid
            if (!isAtEnd() && peek() == 'n') {
                advance();
                return BIGINT;
            }
            return NUMBER;
        }

        // Binary number (0b / 0B)
        if (c == '0' && (peek(1) == 'b' || peek(1) == 'B')) {
            advance(); // 0
            advance(); // b
            int binStart = pos;
            while (!isAtEnd() && (peek() == '0' || peek() == '1')) {
                advance();

View on GitHub (pinned to a22eb90246)