Everybody has the same first reaction, and it doesn’t improve much with experience: the eyes glaze, the stomach drops, and there’s a strong urge to close the window. Twenty lines of red, half of them naming files you’ve never heard of, the whole thing ending in something like TypeError: Cannot read properties of undefined (reading 'map').
It’s worth saying plainly, because it’s the reason this page exists: almost everyone who gives up on working with code gives up during an error. The wall of red reads like a verdict — on the project, and a bit on you. Really it is a machine being extremely literal about the exact point at which it got stuck, and that literalness is precisely what makes the problem solvable.
What’s actually in there
A stack trace is the list of function calls that were in progress at the moment something failed. It looks hostile because it was formatted for a machine — full file paths, line numbers, function names, sometimes counts — but strip that away and it only ever tells you three things:
- The error message — what went wrong, in one line.
- A file and a line number — where it went wrong.
- The chain of calls that led there — how the program got into that position.
That’s the whole vocabulary. Every trace you will ever see, in any language, is those three things wearing different punctuation.
A small thing worth knowing, because it’s where confident advice tends to be wrong: the order of that chain is not consistent between languages. Python prints the deepest call last, so the bottom of a Python traceback is where it broke. JavaScript and Node print the deepest call first, so the top is. Java, Ruby, Go and the rest each have their own convention.
This is exactly why the advice below is what it is. You cannot reliably know which end of a trace matters without already knowing the language’s convention — so don’t try to pick.
The move
You hit an error. You copy the entire thing — every line, including the blank ones and the parts that look like noise — and you paste it into the chat with a sentence about what you were doing. The phrasing genuinely doesn’t matter:
I'm getting this error. What does it mean and where do I look?
[paste the full trace here]
What comes back is the message in plain English, a pointer to the file and line that actually caused it, usually a look at that file, and either a fix or the next thing to check. The scary text becomes a paragraph.
Don’t trim it
This is the one mistake worth naming, because it’s near-universal and it feels like the opposite of a mistake. People paste the last line only — or the one line that looks meaningful — reasoning that they’re removing noise and getting to the point.
What they’re actually removing is the causal chain. The line where an error surfaces is frequently not the line where it originated: something was handed a bad value four calls earlier and nothing complained until it was used. Cut the chain and you’ve deleted the part that explains how a perfectly innocent-looking line ended up failing.
Paste the whole thing. Don’t editorialize, and don’t pre-select the line you think matters — choosing which part is relevant is the diagnostic work, and it’s the part you’re trying to get help with.
If the output scrolled off the screen, scroll back and grab the block from the first blank line above it to the last line of red.
Errors that don’t come with a trace
Plenty don’t. You’ll get a single terse line in a log — command not found, port 3000 already in use — or a failing test showing expected against actual, or a squiggle with a tooltip, or just a red box on a page where a thing was supposed to happen.
Handle all of them the same way: copy the text, and add what you were doing when it appeared.
I asked you to start this project and got this. What's wrong?
[paste the output]
Set that beside “it doesn’t work” and the difference is obvious. Anyone who has tried to help a relative with their computer over text messages already understands this at a deep and personal level.
When it points into someone else’s code
Here’s the moment that makes people give up, and it’s built on a misunderstanding worth dismantling directly.
The trace points somewhere you’ve never been — a framework’s internals, a package you didn’t install deliberately, the guts of the language runtime. The reasonable-sounding conclusion is: I don’t know that code, so I can’t possibly fix this.
You almost never have to. An error that fires inside someone else’s code is nearly always caused by your code using it in a way it didn’t expect.
Concretely: suppose a file has require('./emial') where it should say require('./email'). Starting the project explodes with a wall of red ending in Error: Cannot find module './emial', thrown from somewhere deep inside Node’s module loader — a file you have never opened and have no business opening. Paste the wall in, and the answer is:
“This is thrown by Node’s
requiresystem, but it’s caused byserver/index.js:4— you wroterequire('./emial')and there’s no such file. You meant
One typo, in your file, in a line you wrote. The library was working perfectly the entire time. This is the shape of the overwhelming majority of scary-looking errors, and internalising it is most of what separates people who find debugging tolerable from people who don’t.
The reframe worth keeping
Errors are not punishments, and they’re not a signal that you’re out of your depth. They are the most precise feedback you will ever get from a computer — a machine telling you, with total accuracy and zero tact, the exact point where its instructions stopped making sense.
The reason that used to be cold comfort is that the precision was encoded in a format written for the machine rather than for you. That’s the part that changed. The precision was always useful; now it’s also legible.
After enough of these, a wall of red stops reading as a wall and starts reading as a door. Sometimes an intimidating one. But a door.
Once the code isn’t broken but merely unfamiliar, the companion move is tracing — following a value backward to where it came from, or forward to everything that depends on it.