Home / Articles / Why `catch ()` Throws a SyntaxError in JavaScript

This article is published in English.

Why `catch ()` Throws a SyntaxError in JavaScript

Learn why an empty catch parameter list breaks JavaScript parsing entirely, and see the two grammar-correct ways to write a parameterless catch block.

1836 words

The snippet appears to print "Error" at first glance. In reality the whole file blows up with a SyntaxError, because writing catch () with nothing between the parentheses has never been valid JavaScript.

Picture a second-round front-end interview. What seemed like a warm-up question turned into something trickier. The interviewer asked for a try-catch that throws something and logs a message inside the catch block, then added a hint: "you don't need the error object."

If you don't need the error object, the natural move is to skip declaring it. Years of writing things like function handler() {} train your hands to just leave the parameter list empty:

try {
  throw "Error";
} catch () {
  console.log('Error')
}

Asked what this would output, the obvious answer seems to be "Error" the string gets thrown, the catch block executes, the console.log fires. But when the interviewer had the code actually run:

Uncaught SyntaxError: Unexpected token ')'

Nothing gets printed. Not "Error," not anything else. The script never executes a single statement. That's when the interviewer delivered the line this article takes its title from:

You have five years of experience in JavaScript and don't know how the try-catch block works.

It wasn't phrased as a question. It was a statement, and an uncomfortable one, because it happened to be accurate. Across five years of writing JavaScript, this developer had never once typed catch (). The parameter always had a name, even in cases where it was never actually referenced. The first attempt to omit it revealed a rule that had simply never been learned.

catch has exactly two legal forms

The formal grammar for the catch clause (ECMA-262, section 14.15, The try Statement) is compact:

Catch :
  catch ( CatchParameter ) Block
  catch Block
CatchParameter :
  BindingIdentifier
  BindingPattern

Look closely at the first production. When parentheses are present, a CatchParameter is mandatory inside them. Nothing in the grammar marks it as optional. It has to resolve to exactly one binding: a plain identifier such as e, or a destructuring pattern such as {message}. An empty pair of parens satisfies neither production.

The second production, introduced with ES2019, removes the parentheses completely. Empty parentheses match neither rule. So when the parser reads catch (, it expects a valid binding token next, encounters ) instead, and bails out. That's precisely the message V8 throws: Unexpected token ')'.

This raises a fair question: why does function f() {} compile fine while catch () {} never has? A function's parameter list follows the FormalParameters grammar, which permits zero parameters, default values, and rest syntax. CatchParameter was intentionally designed to be different. It represents exactly one required binding, with no comma-separated list, no defaults, no rest element. Catch has no notion of an empty parameter list, so the mental shortcut of "just empty out the parens" that works for functions doesn't carry over here.

This error happens before your code runs

There was a second misconception buried in this mistake: treating errors as purely a runtime phenomenon. This particular failure occurs during parsing, before execution ever begins. The engine scans the entire script, fails to match it against the grammar, and reports a SyntaxError before a single line has a chance to run. Everything else in that file gets dragged down with it.

Adding one more line makes this obvious. The following was confirmed in Chrome:

console.log('before');            // NEVER runs
try { throw "Error"; } catch() { }
Uncaught SyntaxError: Unexpected token ')'

The console.log('before') sits above the malformed catch clause and has nothing wrong with it on its own, yet it still never prints. The entire script fails to compile, so none of it runs.

This leads to a conclusion that's easy to miss at first: a try-catch block inside a file cannot catch a SyntaxError originating from that same file. For any catch block to run, the surrounding code would already need to have parsed successfully, which is the very thing that failed. The only way to catch this category of error is from a separate compilation unit entirely through eval, new Function, or a dynamic import().

The two ways to write it correctly

Both approaches below were verified in Chrome, and both are valid.

Option 1: keep the parameter, just don't use it. Declaring a catch parameter you never reference is completely legal, and always has been, across every version of ECMAScript.

try {
  throw "Error";
} catch (e) {
  console.log('caught, e unused');
}
// logs: caught, e unused

Option 2: remove the parentheses altogether. This is the ES2019 optional catch binding syntax: no parentheses, no parameter, simply catch {.

try {
  throw "Error";
} catch {
  console.log('caught without binding');
}
// logs: caught without binding

The mental model to hold onto: shortening catch (e) means removing the parentheses, not clearing out what's inside them. The middle ground between those two valid forms is exactly where the SyntaxError lives.

Where the parenless catch syntax originated

The optional catch binding started as a TC39 proposal authored by Michael Ficarra. It reached stage 4 in January 2018 and became part of ES2019. Browser and runtime support followed quickly: Chrome 66, Firefox 58, Safari 11.1, and Node.js 10 all support it, meaning it's safe to use in essentially any environment you'd deploy to today.

The reasoning behind the proposal matches the exact scenario that tripped up the interview candidate: code where the caught error is genuinely never needed. Think of checking whether a string parses as valid JSON and falling back to a default when it doesn't, or feature-detection patterns where merely catching the throw tells you everything you need to know. In cases like these, naming the error as e just creates a variable that gets assigned but never read — and the proposal itself points out that this pattern usually signals a bug elsewhere in the code.

It's worth being precise about what the proposal actually changed: it introduced a new grammar production for a catch block with no parameter list at all. It did not legalize empty parentheses. The parentheses aren't emptied — they're removed entirely. That preserves the rule that CatchParameter, when present, must be exactly one binding, and it makes catch { visually consistent with finally {, a block that never had parentheses in the first place.

Why even experienced developers fall for this

There are three separate reasons this trips people up, and none of them comes down to a lack of effort or study.

Your instincts here come from a different, more familiar pattern — and they mislead you. Every function signature you've ever written reinforces the idea that an unused parameter list can be reduced to empty parens: function () {} is completely normal. Since a catch clause visually resembles a function header, applying the same shortcut feels natural. But a catch clause doesn't take a parameter list — it takes a single binding — and the grammar has simply never included an empty variant of it.

This mistake usually doesn't surface until the exact moment you try to drop an unused e. That moment is often triggered by a linter complaint. ESLint's no-unused-vars rule includes a caughtErrors option, and as of ESLint 9, that option defaults to "all" — meaning an unused catch (e) now produces a lint error out of the box. Trying to fix that warning, developers instinctively empty the parentheses the way they would on a function signature, and that's exactly where the parser stops them. The actually-correct fix — deleting the parens entirely and writing catch { — is the one almost nobody reaches for first, because nowhere else in JavaScript does "shortening" mean deleting rather than emptying.

The bug never survives long enough to leave a mark. A subtle logic error can slip into production and haunt a codebase for months, becoming the kind of war story developers repeat for years. This one is nothing like that: it's a SyntaxError caught immediately at parse time. You see the squiggly underline, fix it in seconds, and move on without a second thought. There's no lasting memory of it — which is exactly why it makes for an effective interview question. It probes the line between syntax you've genuinely internalized and syntax you've only assumed you understand.

One caveat before you go rewriting every catch (e) you can find: catch { } gives you permission to skip naming the error, not to skip handling it. The ES2019 form exists for legitimate probe-and-fallback logic where the mere act of catching is the useful signal. An empty catch body that quietly swallows a genuine failure is just as problematic as it always was, parentheses or not.

The answer that would have landed better

The response that would have kept this interview on track: "This throws a SyntaxError at parse time — specifically Unexpected token ')'. Nothing in the file executes at all, not even code that appears before the try block. A catch clause only has two legal forms: catch (binding) { }, or, since ES2019, the parameter-less catch { }. Empty parentheses don't match either pattern."

Key points worth remembering:

  • catch () with empty parentheses has always been, and remains, a SyntaxError in every version of JavaScript.
  • There are exactly two valid forms: catch (e) { } with a single named binding, or catch { } with no parentheses at all, introduced in ES2019.
  • CatchParameter is a single required binding, not a parameter list — the "empty parens" habit from function syntax simply doesn't transfer.
  • Because this is a parse-time error, the entire file fails to run — a try block elsewhere in that same file can never intercept it.
  • Leaving catch (e) with an unused e is technically legal, but ESLint 9's no-unused-vars flags it by default; the correct fix is removing the parentheses, not clearing what's inside them.
  • The parameter-less catch { } exists for situations where the error value is truly unnecessary — it's not a blanket excuse for silently discarding real failures.

Was the harsh reaction from the interviewer fair? Only partially. The runtime behavior of try-catch was never in question — that part was second nature. What had genuinely never been examined was the grammar itself, simply because everyday working code rarely forces you to confront it. These days, the habit has flipped: reaching to drop an unused e now means deleting the parentheses along with it, not emptying them.

When shortening a catch clause, delete the parentheses — never just empty them.