HOWTO · JavaScript

How to Try Without Catch in JavaScript

In today's post, we'll learn about try statements without implementing catch in JavaScript.

On this page

Yes — a JavaScript try statement can omit catch, as long as a finally clause is present. This tutorial explains when try without catch is valid, what happens to a thrown error, and when this form is the right tool.

How try Works Without catch in JavaScript

The JavaScript try statement encloses code that may throw an error, and it is valid anywhere a statement is allowed: at the top level of a script, inside a function, or nested inside another try. What it cannot do is stand alone — a try block must be followed by a catch clause, a finally clause, or both, so a bare try with neither is a syntax error. The braces {} are always required, even for a single statement.

Syntax:

try {
  // try_statements: code that may throw
} catch (exception_var) {
  // catch_statements: handle the thrown value
} finally {
  // finally_statements: always run afterward
}
  1. try_statements are the statements to be executed.
  2. catch_statements are the statements executed when a value is thrown in the try block.
  3. exception_var is an optional identifier that holds the thrown value for the associated catch block.
  4. finally_statements run after the try statement completes, whether or not a value was thrown or caught.

Because catch is optional only when finally is present (and vice versa), the grammar allows exactly three combinations:

  1. try...catch
  2. try...finally
  3. try...catch...finally

When a catch block is present and a statement inside the try block (or a function it calls) throws, control immediately moves to catch; when nothing is thrown, catch is skipped entirely. The finally block always runs after the try and catch blocks finish, whether or not a value was thrown and whether or not catch handled it.

With no catch clause, finally still runs first and then the thrown value keeps propagating to the caller. This also applies to nesting: try statements can be nested, and when an inner try statement has no catch, the catch block of an enclosing try handles the error instead.

For the full grammar, see the MDN documentation for try...catch.

Example: try...finally Without catch

The snippet below places a try...finally that has no catch of its own inside an outer try...catch, so we can watch the finally block run and see where the thrown value ends up. 0 / 0 evaluates to NaN, so throw new Error(0 / 0) throws an Error whose message is NaN.

Run this example:

try {
  try {
    console.log('Executing try block')
    throw new Error(0 / 0)
  } finally {
    console.log('Final Call')
  }
} catch (error) {
  console.log('Outer catch received: ' + error.message)
}
Executing try block
Final Call
Outer catch received: NaN

The inner try prints its log line and then throws. Because it has no catch, control moves straight to finally, which prints Final Call. Only after finally completes does the Error continue propagating, where the outer catch receives it and prints its NaN message. Without that enclosing catch, the same error would reach the caller or, at the top level, surface as an uncaught error after Final Call prints.

When to Use try Without catch

Reach for try...finally when a block needs guaranteed cleanup but the error itself belongs to the caller. Typical finally jobs include releasing a resource, closing a connection or file handle, restoring shared state such as a re-enabled button or a reset flag, and stopping a timer — work that must happen whether the try block finishes normally or throws.

This form is the right choice when the code has nothing meaningful to add to the error: re-throwing it unchanged adds noise, while an empty catch block swallows every failure silently so bugs surface far from their cause. try...finally keeps the cleanup guarantee and still lets the caller see and handle the error. If the function genuinely needs to inspect the thrown value, log it, or recover, add a real catch clause instead.

Limitations and Pitfalls of try Without catch

  • finally never handles or suppresses the error. After finally finishes, an unhandled thrown value keeps propagating to the caller; at the top level it terminates the script or reports an uncaught error.
  • You cannot observe the thrown value. Without catch, the construct has no access to what was thrown, so it cannot log the message or branch on the error type.
  • Control flow inside finally overrides whatever was pending. A return or throw inside finally discards a value still propagating — including a thrown error, which then disappears silently — so keep finally to simple cleanup that neither returns nor throws.
  • Statements after a try...finally run only when nothing was thrown, and finally itself must not depend on the try block having succeeded, since it also runs on the failure path.