karatelabs/karate · error · ParserException
unexpected character '#' at
Error message
unexpected character '#' at %d:%d
What it means
Karate's JS lexer uses `#` only as the start of a private class field/method name (`#name`), which must be followed by a valid identifier start character. A `#` appearing anywhere else — with no identifier, or after a character that cannot start one — is not part of the JS grammar and is rejected at tokenization time.
Solutions
- Quote the `#` text as a string: `'#'` or `'http://x#frag'`.
- Replace `#` comments with `//` in JS code.
- If a private field was intended, ensure it appears in a class body and `#` is immediately followed by a valid identifier (`#count`).
- Remove the stray `#` if it is a leftover from Karate placeholder syntax used in the wrong position.
Example fix
// before * def x = # // after * def x = '#'
Defensive patterns
Strategy: validation
Validate before calling
// warn about bare # outside strings/class bodies before eval
if (/(^|[^\w'"#/])#(?!\p{ID_Start})/u.test(script)) console.warn('stray # in JS: ' + script); Try / catch
try {
def x = eval(script);
} catch (e) {
if (e.message && e.message.indexOf("unexpected character '#'") >= 0) {
karate.log('# must be quoted or start a private name at ' + e.message);
}
throw e;
} Prevention
- Use // or /* */ for comments in JS, never #.
- Quote any # text (URLs, placeholders) as a string.
- Only use #name private fields inside class bodies.
- Recheck scripts converted from shell/Python comment style.
When it happens
Trigger: A bare `#` or `# ` or `#+` in embedded JS (e.g. `* def x = #`); a `#` used as a comment marker (JS comments are `//` or `/* */`, not `#`); `#` intended as a string left unquoted; a private name like `#1x` starting with a non-identifier character.
Common situations: Coming from shell/Python habits and using `#` for comments inside JS blocks; placing Karate-style `#string` placeholder markers inside actual JS expression context where they are not valid; pasting URLs (`https://...#frag`) unquoted; private-field syntax `#x` used outside a class body.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- invalid binary literal
- invalid escape sequence in template literal: \0
- invalid escape sequence in template literal: \
- invalid hex escape sequence in template literal
- invalid numeric separator
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/5028259bc5810d99.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/parser/JsLexer.java:673
}
}
return keywordOrIdent(tokenStart, pos - tokenStart);
}
// `#` + IdentifierName, one token including the hash. A bare `#` has no other
// role in the grammar, so anything else is a hard error rather than the lenient
// IDENT fallback — a half-lexed `#x` would parse into silently wrong garbage.
private TokenType scanPrivateName() {
advance();
if (pos < length) {
char c = source.charAt(pos);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$'
|| (c > 127 && Character.isJavaIdentifierStart(c))) {
scanIdentifier();
return PRIVATE_NAME;
}
}
throw new ParserException(String.format(
"unexpected character '#' at %d:%d", tokenLine + 1, tokenCol + 1));
}
private TokenType keywordOrIdent(int start, int len) {
// Note: "this" is NOT a keyword in the lexer - it's an identifier that gets
// special handling in the parser/evaluator. "void" lexes as the VOID keyword
// so the parser can dispatch it as a unary operator.
// Fast path: switch on length first, then first char
if (len == 2) {
char c0 = source.charAt(start);
if (c0 == 'i') {
if (source.charAt(start + 1) == 'f') return IF;
if (source.charAt(start + 1) == 'n') return IN;
} else if (c0 == 'o' && source.charAt(start + 1) == 'f') {
return OF;
} else if (c0 == 'd' && source.charAt(start + 1) == 'o') {
return DO;
}View on GitHub (pinned to a22eb90246)