Error handling with try...catch
JavaScriptThe short answer
try...catch lets you handle errors gracefully without crashing your program. You put the risky code inside try, and if it throws an error, the catch block runs instead. There is also a finally block that runs no matter what — whether there was an error or not.
How it works
try { const data = JSON.parse('invalid json');} catch (error) { console.log('Parsing failed:', error.message);}console.log('Code continues running');Without try...catch, the JSON.parse would crash the program. With it, the error is caught, handled, and the program keeps running.
The structure
try { // code that might throw an error} catch (error) { // runs only if an error was thrown // error contains information about what went wrong} finally { // always runs — with or without error}The error object has useful properties:
error.message— a description of the errorerror.name— the type of error (likeTypeError,SyntaxError)error.stack— the stack trace showing where the error happened
Throwing your own errors
You can throw errors yourself using the throw keyword:
function divide(a, b) { if (b === 0) { throw new Error('Cannot divide by zero'); } return a / b;}try { const result = divide(10, 0);} catch (error) { console.log(error.message); // "Cannot divide by zero"}You can throw any value, but it is best practice to throw Error objects because they include a stack trace.
Different error types
JavaScript has built-in error types for different situations:
// TypeError — wrong typenull.toString(); // TypeError: Cannot read properties of null// ReferenceError — variable does not existconsole.log(x); // ReferenceError: x is not defined// SyntaxError — code is not valid JavaScript// JSON.parse('{invalid}'); // SyntaxError// RangeError — number out of rangenew Array(-1); // RangeError: Invalid array lengthYou can check the error type in your catch block:
try { // some code} catch (error) { if (error instanceof TypeError) { console.log('Type error:', error.message); } else if (error instanceof ReferenceError) { console.log('Reference error:', error.message); } else { console.log('Unknown error:', error.message); }}The finally block
finally runs no matter what. It is useful for cleanup work like closing connections or clearing timers:
function readData() { let connection = null; try { connection = openDatabase(); const data = connection.query('SELECT * FROM users'); return data; } catch (error) { console.log('Query failed:', error.message); return []; } finally { // This runs even after return if (connection) { connection.close(); } }}Even though there is a return inside try or catch, the finally block still runs before the function actually returns.
try...catch does not catch async errors
This is an important point. try...catch only catches synchronous errors. It does not catch errors in callbacks or promises:
// This does NOT worktry { setTimeout(() => { throw new Error('Async error'); }, 1000);} catch (error) { // This never runs! console.log(error);}The setTimeout callback runs later, after the try...catch has already finished.
For async code, you need to use try...catch inside an async function with await:
async function fetchData() { try { const response = await fetch('/api/data'); const data = await response.json(); return data; } catch (error) { console.log('Fetch failed:', error.message); }}Or use .catch() with promises:
fetch('/api/data') .then((response) => response.json()) .catch((error) => { console.log('Fetch failed:', error.message); });Common Pitfalls
A common mistake is wrapping everything in try...catch. Only wrap code that can actually throw an error. Too many try...catch blocks make code harder to read and can hide real bugs. Also, never leave a catch block empty — always handle or log the error. Silent failures are the worst kind of bugs.
Interview Tip
When explaining try...catch, make sure to mention that it does not work with asynchronous callbacks. Show both the synchronous and async patterns. Interviewers often follow up by asking about finally — explain that it always runs and is used for cleanup. Knowing these details shows practical experience with error handling.
Why interviewers ask this
Error handling is a critical part of writing production code. Interviewers want to see if you know how to handle errors properly, if you understand the difference between sync and async error handling, and if you follow best practices like not swallowing errors silently. Good error handling separates production-ready code from code that breaks unexpectedly.