Back to Blog
Mar 25, 20264 min readOnuzulike Anthony

Reading Technical Errors Like a Senior Engineer

A framework for diagnosing stack traces, TypeScript errors, and runtime panics — and the mental models that let experienced engineers fix bugs faster.

EngineeringDebuggingEngineeringTypeScriptBest Practices

Junior engineers read the first line of an error message. Senior engineers read the last. The difference is knowing where the actual problem lives versus where the symptom surfaced.

Stack Traces: Start from the Bottom

A stack trace reads from innermost (bottom) to outermost (top). The first line is the generic error category; the bottom entries are your code. The middle is library internals that rarely help.

TypeError: Cannot read properties of undefined (reading 'map')
    at MDXRemote (/node_modules/next-mdx-remote/dist/index.js:47:21)
    at renderWithHooks (/node_modules/react-dom/cjs/react-dom.development.js:14985:18)
    at mountIndeterminateComponent (/node_modules/react-dom/...)
    at BlogPostPage (src/app/blog/[slug]/page.tsx:28:10)   ← YOUR CODE
    at processChild (/node_modules/next/...)

Skip the React and Next.js internals. The error is at page.tsx:28:10. Open that file, go to line 28. Something there is passing undefined where MDXRemote expects an object. The problem is in your code, not in MDXRemote.

TypeScript: Read the Full Error Chain

TypeScript errors are backwards. The error that matters is usually the last one in the chain, not the first:

Type '{ category?: string | undefined; }' is not assignable to type 'BlogPost'.
  Type '{ category?: string | undefined; }' is missing the following properties
  from type 'BlogPost': slug, title, description, date, content (5 more)

Don't try to fix category: string | undefined. The real problem is that you're returning an incomplete object somewhere that needs a full BlogPost. Find where that return value is constructed and add the missing fields.

"String to replace not found" in Edit Calls

This error means the string you tried to edit doesn't match what's in the file. Causes:

  1. Different whitespace (tabs vs spaces, trailing spaces)
  2. The file was already modified after you last read it
  3. Copy/paste introduced Unicode characters that look like ASCII

Fix: read the exact lines with Read offset/limit, then use the exact string as it appears in the file. Never construct the string from memory.

Hydration Errors in Next.js

Error: Hydration failed because the initial UI does not match what was rendered on the server.

The HTML rendered on the server doesn't match what React renders in the browser. Common causes:

  1. typeof window !== 'undefined' in render — produces different output server vs client. Move window-dependent code into useEffect.
  2. Dates formatted with toLocaleDateString() — locale differs between server and browser. Format dates explicitly: new Date(date).toISOString().slice(0, 10).
  3. Random or timestamp values — generated differently each run. Compute once, pass as props.

Import Errors: Module Resolution

Cannot find module '@/components/ui/button' or its corresponding type declarations.

Read in this order:

  1. Does the file exist at that path?
  2. Does tsconfig.json have the correct paths alias ("@/*": ["./src/*"])?
  3. Is the component exported (not just defined)?
  4. Did you restart the TypeScript server after adding the file?

Most import errors are one of these four.

Async/Await Errors

UnhandledPromiseRejectionWarning: Error: ...

This means a Promise rejected and nobody caught it. Two common patterns:

typescript
// Wrong — error in the async function is swallowed
someAsyncFn().then(result => {
  doSomethingWith(result);
});
 
// Right
someAsyncFn().then(result => {
  doSomethingWith(result);
}).catch(err => {
  console.error("someAsyncFn failed:", err);
});
 
// Or with async/await
try {
  const result = await someAsyncFn();
  doSomethingWith(result);
} catch (err) {
  console.error("someAsyncFn failed:", err);
}

If the rejection is in a setTimeout or setInterval, it won't be caught by try/catch — wrap the callback itself.

Build Errors vs Runtime Errors

Build errors: TypeScript type errors, missing imports, invalid syntax. Safe to iterate on — they won't affect production until you ship.

Runtime errors: happen during execution, often only on certain inputs. Harder to reproduce, require logs or error monitoring.

For Next.js: build errors appear in npm run build. Runtime errors appear in browser devtools or server logs. Never ship a build with TypeScript errors — they exist for a reason.

The Three-Question Framework

When hitting any error:

  1. Where is the actual error? — Read the stack trace bottom-up, find your code, not library internals.
  2. What type mismatch or missing value caused it? — Most errors reduce to "X is undefined" or "X is not the shape I expected."
  3. Where is X constructed? — Trace backwards from the error site to where the bad value was created. Fix it there, not where it was used.

This framework applies to TypeScript errors, runtime exceptions, and build failures equally. Fixing symptoms (adding ?. everywhere) is slower than finding the source.