How to catch errors in synchronous functions in node.js?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
For synchronous Node.js code, error handling is straightforward: if a function throws, catch it with try and catch in the same call stack. The important limit is that try/catch only handles errors thrown synchronously. Once the code crosses into callbacks or promises, the error-handling model changes, which is why many “why did this not get caught” bugs are really timing-model misunderstandings.
Catch Thrown Errors with try/catch
If a synchronous function throws an error, wrap the call site in try/catch:
This works because parsePort() throws before control leaves the current synchronous execution path. The exception bubbles up until a matching catch block handles it.
The same pattern applies to built-in synchronous APIs such as JSON.parse() or fs.readFileSync(). If the operation throws immediately, try/catch is the correct tool.
Keep the try Block Focused
A small try block is usually better than wrapping half the application:
This keeps the failure surface clear. If the block is too large, it becomes harder to tell which operation actually caused the exception.
You can also rethrow after adding context:
That is useful when a low-level error needs a more domain-specific message.
try/catch Does Not Catch Async Failures
This is where many bugs come from. A try/catch around a synchronous function call will not catch errors that occur later inside asynchronous code:
The catch block never runs because the error happens in a later event-loop turn. For callbacks, promises, and async functions, use the error-handling mechanism that belongs to that model.
For example, with promises:
Use finally for Cleanup
If synchronous code opens resources or acquires temporary state, finally ensures cleanup runs whether the operation succeeds or fails:
finally is not a replacement for error handling, but it is the right place for guaranteed cleanup.
Common Pitfalls
The biggest mistake is expecting try/catch to handle asynchronous errors automatically. It does not. It only catches errors thrown before the current synchronous stack unwinds.
Another issue is writing very large try blocks. That makes debugging harder because unrelated failures are all handled in one place with no clear boundary. Small, focused try blocks make logs and stack traces much easier to interpret later.
Some code also catches an error and silently ignores it. Unless the error is truly expected and harmless, either log it, handle it meaningfully, or rethrow it.
Finally, avoid using exceptions for ordinary control flow. Throw when the state is genuinely exceptional, not as a substitute for normal branching logic.
Summary
- Use
try/catchto handle errors thrown by synchronous Node.js functions. - Catch at the call site or add context before rethrowing.
- Keep
tryblocks narrow so failures stay easy to diagnose. - Use
finallyfor cleanup that must always run. - Do not expect
try/catchto catch errors that happen later in async code.

