{"record":{"id":"5ec091ff62754d0f","repo":"karatelabs/karate","slug":"invalid-numeric-separator","errorCode":null,"errorMessage":"invalid numeric separator","messagePattern":"invalid numeric separator","errorType":"exception","errorClass":"ParserException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/parser/JsLexer.java","lineNumber":459,"sourceCode":"\n    // ========== Numbers ==========\n\n    private TokenType scanNumber() {\n        char c = peek();\n\n        // Hex number\n        if (c == '0' && (peek(1) == 'x' || peek(1) == 'X')) {\n            advance(); // 0\n            advance(); // x\n            int hexStart = pos;\n            // Fast path: hex digits only, no separator\n            while (!isAtEnd() && isHexDigit(peek())) {\n                advance();\n            }\n            // Rare path: separator(s) inside hex literal — must follow a digit, not the prefix\n            if (!isAtEnd() && peek() == '_') {\n                if (pos == hexStart) {\n                    throw new ParserException(\"invalid numeric separator\");\n                }\n                scanHexDigitsWithSeparators();\n            }\n            // Rare path: BigInt suffix — `0xff_ffn` is valid\n            if (!isAtEnd() && peek() == 'n') {\n                advance();\n                return BIGINT;\n            }\n            return NUMBER;\n        }\n\n        // Binary number (0b / 0B)\n        if (c == '0' && (peek(1) == 'b' || peek(1) == 'B')) {\n            advance(); // 0\n            advance(); // b\n            int binStart = pos;\n            while (!isAtEnd() && (peek() == '0' || peek() == '1')) {\n                advance();","sourceCodeStart":441,"sourceCodeEnd":477,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/parser/JsLexer.java#L441-L477","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Remove the underscore after the prefix: write 0xF0F0 instead of 0x_F0F0","Place separators only between digits: 0xFF_FF is valid","Validate generated literal strings so the digit part is never empty","If the value comes from data, parse it with parseInt(str, 16) instead of embedding it as a literal"],"exampleFix":"// before\nvar mask = 0x_F0F0;\n// after\nvar mask = 0xF0F0;   // or 0xF0_F0 for readability","handlingStrategy":"validation","validationCode":"if (/^0[xX]_/.test(literalSrc)) throw new Error('numeric separator cannot follow the 0x prefix');","typeGuard":"null","tryCatchPattern":"try {\n  karate.eval('var n = ' + literalSrc + ';');\n} catch (e) {\n  if (String(e.message).indexOf('invalid numeric separator') >= 0) {\n    literalSrc = literalSrc.replace(/^0[xX]_/, '0x'); // strip bad separator and retry\n  } else throw e;\n}","preventionTips":["Place _ separators only between digits, never after 0x/0b/0o prefixes","Validate generated numeric literals with a regex before embedding","Parse external hex strings with parseInt(str, 16) instead of literal interpolation","Enable JS linting on any generated script content"],"tags":["javascript","lexer","numeric-literal","numeric-separator"],"backgroundTag":"invalid-numeric-literal","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}