Stack traces are intimidating until you have a method. The most common way one lands in your day is not alone at your own desk: a teammate pastes a wall of red into a shared chat or a live session and asks, "any idea?" Now two of you are staring at the same unfriendly text. The good news is that a stack trace is a precise instrument that, read properly, points within seconds at the line of code you need to change — and two people reading it together get there faster than one.
The method below is six small moves. None of them are clever; together they make any stack trace — JavaScript, Python, anything — readable. The same routine applies whether you are debugging solo or looking at a trace a colleague just dropped in front of you, so we will walk the six steps first and then show how to split them between two readers in a shared session.
What a stack trace is actually telling you
When an error is thrown, the runtime captures the chain of function calls that led to the throw and prints them in order from most-recent to least-recent. The top frame is the function that actually threw. Each frame below it is the function that called the one above it. The bottom frame is wherever the program started — a request handler, a button click, a script entry point.
The trace is not the error. The error is the message at the top, usually one short line. The trace is the breadcrumb path the runtime walked to get there. Read the message first; read the trace second.
The six-step routine
Step 1. Read the error message at the top
The first line of the output usually contains everything you need to identify the category of bug: TypeError, ReferenceError, ConnectionRefusedError, ValueError. Read it twice. The category alone tells you whether you are looking at a typo, a logic bug, a missing dependency, or an infrastructure failure.
Step 2. Read the second line — the specific cause
The second line names the specific value that triggered the error. Cannot read properties of undefined (reading 'name') tells you which property was missing. list index out of range tells you the access was past the end of an array. Get the specific value into your head before looking at any frame.
Step 3. Find the first frame in your own code
The top frame is often in a library — node_modules, the React runtime, the Firebase SDK. That is not where the bug lives. The bug lives at the boundary between your code and the library. Scan down the trace until you find the first file path that points at your source files. That frame is the one to read carefully. On ShareCode, whose editor runs on React and the Firebase SDK, the traces we chase most often bury our own first frame a dozen lines down under SDK internals — finding that frame is consistently the fastest move.
TypeError: Cannot read properties of undefined (reading 'id')
at applyFilters (node_modules/react-query/lib/...) ← library
at QueryCache._notify (node_modules/react-query/...) ← library
at QueryObserver.<anonymous> (node_modules/...) ← library
at UserList (src/components/UserList.tsx:42) ← YOUR FRAME
at renderWithHooks (node_modules/react-dom/...)
at mountIndeterminateComponent (...)
In this trace, the bug is at line 42 of UserList.tsx. The frames above it are how the library got there; the frames below are how React got to your component. You only need to look at line 42.
Step 4. Open the named file at the named line
Open the file. Look at the exact line. Find the value that the error message named. In our example, line 42 of UserList.tsx is reading .id off something that turned out to be undefined. The next question is: where did that something come from?
Step 5. Walk upstream from the failing line
The failing line is the point of impact, not the source. Look at where the variable was assigned. Is it the result of an array access that might have returned undefined? Is it a prop that the parent component might have failed to pass? Is it a fetch response that might have come back empty? Walk one step upstream at a time until you find the place where the value first became wrong.
Step 6. Reproduce, then fix one thing
Before changing code, reproduce the failure. Run the exact path that triggered the trace and confirm the same error appears. Then change exactly one thing — add a null check, fix the upstream assignment, correct the prop type — and run the same path again. If the error is gone, you fixed it. If a different error appears, you have made progress: a new error usually means the original bug is fixed and a downstream bug has been exposed.
Worked example
Here is the same routine applied to a real-shaped trace from a Python service:
Traceback (most recent call last):
File "/app/server.py", line 88, in handle_request
return self.process(payload)
File "/app/service.py", line 41, in process
user = self.users[payload["user_id"]]
KeyError: 'user_id'- Step 1. KeyError — a dictionary lookup failed. This is a logic bug, not a typo or infrastructure issue.
- Step 2. Specific value:
'user_id'. The key being looked up wasuser_idand it was not present in whatever dictionary this is. - Step 3. First frame in our code: line 41 of service.py. Both frames are ours here, so the boundary is the
payload["user_id"]access. - Step 4. Line 41 reads
user_idfrompayload.payloadis missing that key. - Step 5. Walk upstream.
payloadcomes from the request body in handle_request. The bug is either a client sending the wrong field name (validation issue) or our code expecting a field that the API contract does not require (assumption issue). - Step 6. Reproduce by sending a request without
user_id, then fix one thing — add validation at the request boundary that rejects the request with a clear 400 response instead of crashing with a KeyError.
Reading a trace together
The scenario that started this post is the everyday one: a teammate pastes a trace into a shared session and says "any idea?" The instinct is for both people to scroll the same wall of text in parallel and narrate over each other. Splitting the work is faster. One person reads top-down for the throw site — the error message and the first frame that actually raised it. The other follows the chain downward, tracing any Caused by: sections or the async boundaries that stitch one trace onto another, so the group sees both the impact point and the path that led there at the same time.
The move that makes this concrete is sharing line numbers, not screenshots. Paste both the trace and the offending file into the same editor so everyone is looking at the same line 42, not a description of it. The senior engineer's real contribution is rarely a flash of insight — it is pointing at the first line that is in your code, past the framework frames everyone else was still reading. Once that line is named, the rest of the routine (open it, walk upstream, reproduce, fix one thing) runs the same as it does solo.
This is exactly what ShareCode is for. Drop the trace and the file into a code space, share the URL — no install, no signup — and both people are in the same document with live cursors. When you point at the offending line, the other person sees your cursor land on it, with no screen-share lag and no "scroll up a bit, no, the other one." Two readers, one shared line, the six-step method between you.
The habit that compounds
The first dozen stack traces you read carefully will feel slow. The next hundred will feel automatic. After a year of running this routine on every error you encounter, you will be reading traces faster than the runtime can finish printing them, and the intimidation factor will be gone for good. The discipline that gets you there is small but non-negotiable: never paste a trace into a search engine before you have read it yourself.
Related reading
Reading stack traces is one of the compounding skills covered in the engineering-foundations topic. For the broader JavaScript debugging toolkit, the JavaScript debugging guide covers conditional breakpoints, async stack traces, and the DevTools moves that go beyond reading the trace itself.
References & Sources
The primary sources, specifications, and documentation behind this article. Each link opens in a new tab.
- Error.prototype.stack
MDN contributors · MDN Web Docs
Defines the JavaScript stack string this post teaches you to read, including how frame format differs across engines.
developer.mozilla.org - Stack trace API
V8 Team · V8
Authoritative source on how V8 (Chrome, Node.js) builds the trace, plus the zero-cost async stack traces referenced in the FAQ.
v8.dev - traceback — Print or retrieve a stack traceback
Python Software Foundation · Python Documentation
The reference for the Python traceback format used in the worked KeyError example.
docs.python.org - Errors and Exceptions
Python Software Foundation · Python Documentation
Explains the 'most recent call last' ordering and the caret markers that step 1 of the routine relies on.
docs.python.org - Debug your original code instead of deployed with source maps
Chrome DevTools Team · Chrome for Developers
Backs the production-trace FAQ: source maps translate minified frames back to the original file and line.
developer.chrome.com
About the writers
Founder of ShareCode. Writes the engineering deep-dives on this site — WebRTC, Firebase Auth, real-time sync, and the production patterns behind the editor itself.
More from Kishan
Developer educator at ShareCode. Writes the tutorial track — Python, JavaScript debugging, coding-interview prep, and the everyday code-quality habits that hold up in real codebases.
More from Kajal
Stuck on a trace you can't crack?
Paste the trace and the surrounding code into a code space, share the link with a colleague, and walk through the six-step routine together. Most stuck traces unstick within the first three steps when a second reader is in the document.
Open a code space →