JS 'Invalid class name' error in Linux terminal apps
This error shows up when Node.js tries to parse a CSS class name that starts with a number or has special chars. The fix is to use a valid class selector.
You're running a Node.js script on your Linux box — maybe a web scraper with cheerio or a DOM parser using jsdom. Everything works fine until you hit a page with a class like .123abc or .foo-bar!. Then you get:
DOMException: Failed to execute 'querySelector' on 'Document': '.123abc' is not a valid selector.
Or something similar with Invalid class name in the error message. This isn't a bug in your code. It's a CSS selector spec rule that Node.js strict mode enforces.
Root cause
What's actually happening here is that CSS class names must follow specific syntax rules. The CSS Selectors Level 3 spec says a class selector must be a valid identifier. An identifier can't start with a digit, and certain characters like !, @, #, or colons aren't allowed unless escaped.
When you use document.querySelector('.123abc'), the browser (or jsdom) sees that dot followed by a digit and says "nope, that's not a valid selector." The same goes for .foo!bar — the exclamation mark isn't valid in an identifier. It's not your fault, the HTML author broke the rules. But Node.js parsers enforce the spec strictly, while browsers are more forgiving in practice.
The fix
- Escape the invalid character using CSS.escape() — the proper way. For example:
const className = '.123abc'; const safeSelector = CSS.escape(className.slice(1)); // '\\31 23abc' document.querySelector('.' + safeSelector);CSS.escape()handles all edge cases — digits at start, hyphens, colons, you name it. It's built into modern browsers and jsdom (if you enable thecssmodule). - Use attribute selectors instead — simpler for quick scripts. Since classes are stored as attributes, you can bypass the class selector rule:
This matches elements where the exact class attribute equalsdocument.querySelector('[class="123abc"]');123abc. Downside: it matches the full class string, so elements with multiple classes won't match unless the order is identical. - For partial class matches — use
class~=:
The tilde-equals (document.querySelector('[class~="123abc"]');~=) selector matches any element where123abcappears as a space-separated token in the class attribute. This is closer to what.123abcwould do if it were valid. - If you're using cheerio — cheerio's
.find('.123abc')will throw a similar error. Use.filter('[class="123abc"]')or.find('[class~="123abc"]')instead.
What to check if it still fails
- Check if the class name has escaped characters in the HTML itself — some servers send class names with backslashes or hex codes. For example,
.\\31 23abcis actually the escaped version of.123abc. If you're copying from devtools, you might see the escaped form. Try using the raw attribute value instead. - Make sure the element actually exists — the error only triggers when you try to use an invalid selector. If
querySelectorreturns null because the class doesn't exist, that's a different error (usuallyUncaught TypeError: Cannot read properties of null). - If you're using a library like jsdom, verify you're passing the correct options. In jsdom 16+, you need
{ runScripts: "outside-only" }to haveCSS.escapeavailable. Without that, you'll get aReferenceError. - On older Node.js versions (pre-14),
CSS.escape()isn't available. Use a polyfill or stick to attribute selectors. I'd just upgrade Node.js though — you're running ancient software otherwise. - If the class name contains a colon like
.foo:bar, browsers interpret the colon as a pseudo-selector (like:hover). You can't fix this with escaping alone — you must use the attribute selector approach:[class="foo:bar"].
The real lesson here: don't trust class names from external sources. Sanitize or escape them before using them in selectors. Attribute selectors are your friend when dealing with dodgy HTML. They're not as pretty, but they work every time.
Was this solution helpful?