JavaScript
Error Tracking
Debugging
Function Calls
Code Maintenance

JS How to track errors where there are many functions calls

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

When a JavaScript program calls many layers of functions, the hard part is usually not catching an error but preserving enough context to understand where it came from. The practical tools are stack traces, structured logging, and consistent error wrapping so failures carry both the original cause and the business context that led to it.

Start with Stack Traces

In JavaScript, thrown Error objects already capture a stack trace in modern runtimes. That means the first rule is simple: throw real Error instances, not raw strings or arbitrary objects.

javascript
1function parseOrder(jsonText) {
2  return JSON.parse(jsonText);
3}
4
5function loadOrder(rawText) {
6  try {
7    return parseOrder(rawText);
8  } catch (error) {
9    throw new Error(`loadOrder failed: ${error.message}`);
10  }
11}
12
13try {
14  loadOrder("not valid json");
15} catch (error) {
16  console.error(error.stack);
17}

The stack tells you where the exception moved through the call chain. That alone is often enough for synchronous code.

Add Context Without Hiding the Original Failure

In larger systems, "unexpected token" is not actionable by itself. You need to know which request, user, file, or operation triggered the parser. The goal is to add context while preserving the underlying cause.

In modern runtimes you can include a cause:

javascript
1function saveUserProfile(input) {
2  try {
3    return JSON.parse(input);
4  } catch (error) {
5    throw new Error("saveUserProfile received invalid JSON", { cause: error });
6  }
7}
8
9try {
10  saveUserProfile("{bad json}");
11} catch (error) {
12  console.error(error.message);
13  console.error("Cause:", error.cause?.message);
14  console.error(error.stack);
15}

This pattern is much better than catching an error and replacing it with a vague new one that loses the original detail.

Log Structured Data at Important Boundaries

When there are many function calls, you do not want to log every function entry because that creates noise. Log at meaningful boundaries instead:

  • Request handlers.
  • Queue consumers.
  • Database or API calls.
  • Background job entry points.

Use consistent fields such as request ID, user ID, operation name, and payload size. That makes correlation possible when a stack trace alone is not enough.

javascript
1function logError(context, error) {
2  console.error(JSON.stringify({
3    level: "error",
4    operation: context.operation,
5    requestId: context.requestId,
6    message: error.message,
7    stack: error.stack
8  }));
9}

Structured logs are much easier to search than free-form console text once the application grows.

Async Code Needs Special Attention

With promises and async functions, failures can cross event loop boundaries. The good news is that modern runtimes preserve async stack traces reasonably well, but only if you await or return promises correctly.

javascript
1async function fetchUser() {
2  throw new Error("database timeout");
3}
4
5async function buildPage() {
6  try {
7    await fetchUser();
8  } catch (error) {
9    throw new Error("buildPage failed", { cause: error });
10  }
11}
12
13buildPage().catch((error) => {
14  console.error(error.stack);
15});

Unawaited promises are a common reason errors seem to disappear or show up without the right context. If a function starts async work, either await it or return the promise to a caller that will handle it.

Use a Central Reporting Layer

For production systems, send uncaught exceptions and handled operational failures to a central service such as Sentry, Datadog, or your own logging pipeline. The important design choice is consistency: one wrapper around window.onerror, unhandledrejection, or the server framework's error middleware is much more useful than scattered ad hoc logging.

Even without a third-party service, a central reporter gives you one place to normalize metadata and deduplicate noisy events.

Debug the Call Chain, Not Just the Failing Line

When many functions are involved, the bug may not be at the line that threw. It might be a bad input several frames earlier. That is why preserving inputs, correlation IDs, and cause chains matters. A stack trace explains where the failure surfaced. Context explains why the call chain reached that state.

Common Pitfalls

  • Throwing strings instead of Error objects.
  • Catching an error and replacing it without keeping the original cause.
  • Logging every function call and creating unusable noise.
  • Forgetting to await or return a promise, which breaks error flow.
  • Treating stack traces as enough when the missing piece is request context.

Summary

  • Throw Error objects so stack traces remain useful.
  • Add business context while preserving the original cause.
  • Log structured metadata at system boundaries, not everywhere.
  • Handle promises consistently so async errors stay visible.
  • Use a central reporting path for production error tracking.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.