5 exercises — Python, Java, and Node.js stack traces. Identify the root exception, locate the application code frame, distinguish library internals from your bugs, and recognise recursion errors.
0 / 5 completed
Stack trace reading strategy (any language)
Step 1: Read the exception name and message — understand WHAT failed
Step 2: Find your application's frames — look for your package/path (not node_modules, not java.lang, not site-packages)
Step 3: The top-most app frame = where you entered the call chain; the bottom-most = closest to the crash
Step 4: Library frames below your code = proximate cause; look above for the application bug
Repeated frame: infinite recursion — find the missing base case or cycle
1 / 5
A Python stack trace ends with: Traceback (most recent call last): File "app.py", line 47, in process_order result = db.execute(query, params) File "/usr/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1774, in execute return self._exec_single_context(...) File "/usr/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1822, in _exec_single_context self.dialect.do_execute(...) sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) SSL connection has been closed unexpectedly
Which line should an on-call engineer look at first to understand what went wrong in the application code?
How to read a Python stack trace:
The stack trace reads from top to bottom in call order — the most recent call is at the bottom. But the application code to investigate is the first non-library frame from the bottom.
Reading strategy: 1. Read the last line first — this is the exception class and message: sqlalchemy.exc.OperationalError: SSL connection closed unexpectedly → tells you WHAT failed 2. Find the first app.py frame from the bottom — line 47 in process_order → tells you WHERE in your code it happened 3. Everything between them is library internals — usually not the bug
Clues in this trace: • The exception is OperationalError (database connectivity), not DataError (bad data) or IntegrityError (constraint violation) • "SSL connection closed unexpectedly" → the database closed the connection mid-query • Possible causes: database restart, connection timeout, network drop, idle connection reclamation by the database
Stack trace vocabulary: • Traceback (most recent call last) — Python's standard header • File "...", line N, in function_name — one frame in the call stack • Last line — the exception type and message • Frame — one level of the call stack • Root cause vs proximate cause — the SSL drop is the proximate cause; the root cause may be a database restart
2 / 5
A Java stack trace contains: java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null at com.example.api.UserService.validateInput(UserService.java:89) at com.example.api.UserController.createUser(UserController.java:34) at com.example.api.UserController$$FastClassBySpringCGLIB$$1.invoke(Unknown Source) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:897)
A colleague says "this is a Spring bug." Is this correct? Where is the actual issue?
A NullPointerException is almost always application code trying to use a reference that was never assigned or was returned as null.
Reading Java stack traces: Unlike Python, Java stack traces read bottom-to-top (bottom = first call, top = most recent). The exception and its message are at the top.
The signal in this trace: • NullPointerException: Cannot invoke "String.length()" because "str" is null • Modern Java (14+) tells you exactly which variable is null — "str" • at com.example.api.UserService.validateInput(UserService.java:89) — your code, your class, your line • Everything below it (SpringCGLIB, ReflectiveMethodInvocation, FrameworkServlet) is the Spring framework infrastructure that routed the request to your method
The $$FastClassBySpringCGLIB$$ mystery: This is Spring's dynamic proxy mechanism — Spring wraps your controller methods with AOP proxies for transaction management, security checking, etc. You will see this in almost every Spring stack trace. It is not the bug.
Rule of thumb: In a stack trace, look for frames matching your package (e.g., com.example.api). Those are your code. Everything else is framework infrastructure.
Java exception vocabulary: • NullPointerException — null reference used where an object was expected • IllegalArgumentException — invalid argument passed to a method • ClassCastException — wrong type used • ArrayIndexOutOfBoundsException — array index beyond array size • StackOverflowError — infinite recursion (not an Exception — it's an Error)
3 / 5
A Node.js/JavaScript error shows: TypeError: Cannot read properties of undefined (reading 'email') at formatUser (/app/utils/format.js:23:28) at Array.map (<anonymous>) at getUsers (/app/routes/users.js:45:29) at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5) at next (/app/node_modules/express/lib/router/route.js:144:13)
What is the most likely cause and where in the code should you look?
Reading Node.js stack traces:
Same top-to-bottom pattern as Java — exception at top, call chain after.
Decoding this specific error: "Cannot read properties of undefined (reading 'email')" means: • You have code like obj.email • obj is undefined (not null — actually undefined) • JavaScript says: can't read property 'email' of undefined
The call chain tells the story: 1. getUsers in users.js:45 builds an array and calls .map(formatUser) 2. Array.map calls formatUser for each item 3. One of those items is undefined 4. formatUser tries to access item.email on line 23 → crash
Where to fix it: Two potential fixes: 1. In users.js: filter out undefined/null items before mapping: .filter(Boolean).map(formatUser) 2. In format.js: add a guard: if (!user) return null;
JavaScript error vocabulary: • TypeError: Cannot read properties of undefined — classic null/undefined access • TypeError: X is not a function — called something that isn't a function • ReferenceError: X is not defined — variable doesn't exist in scope • SyntaxError — code couldn't be parsed (typo, missing bracket) • <anonymous> in stack trace — an arrow function or callback with no name
4 / 5
An error monitoring tool shows a stack trace with this header: RuntimeError: maximum recursion depth exceeded File "task_runner.py", line 156, in resolve_dependencies return resolve_dependencies(dep, visited) File "task_runner.py", line 156, in resolve_dependencies return resolve_dependencies(dep, visited) [Previous line repeated 994 more times]
What does this stack trace tell you about the nature of the bug?
RecursionError / maximum recursion depth exceeded always means infinite or very deep recursion.
What the repeated frame tells you: "[Previous line repeated 994 more times]" is Python's way of saying: the same function called itself ~1000 times without a base case stopping it.
The clue is in the function name: resolve_dependencies — dependency resolution typically uses depth-first graph traversal. If the dependency graph has a circular dependency (cycle), a naive recursive resolver will loop forever: • Task A depends on Task B • Task B depends on Task A • resolve_dependencies(A) → resolve_dependencies(B) → resolve_dependencies(A) → ...
Why "visited" might not be working: • visited is not being passed correctly through recursive calls • visited is a mutable default argument (Python bug: def f(visited=[]) — this is shared across all calls) • The cycle detection check has an off-by-one or wrong comparison
How to confirm a cycle: Add logging before the recursive call: log which dependency is being resolved. You will see the same pair repeating.
Recursion error vocabulary: • maximum recursion depth exceeded (Python) = StackOverflowError (Java/JVM) = RangeError: Maximum call stack size exceeded (JavaScript/Node.js) • base case — the condition that stops recursion • cycle detection — the visited set that prevents revisiting nodes in a graph • circular dependency — A depends on B which depends on A
5 / 5
You receive a stack trace from a different team. It contains 47 frames, mostly from frameworks and libraries. The team asks: "Where is the bug?" What is the correct approach to identify the application code frames quickly?
Efficient stack trace reading strategy:
Step 1: Read the exception name and message (always first) This tells you WHAT failed before you know WHERE. Examples: NullPointerException, ConnectionRefused, KeyError: 'user_id', SSL handshake failed
Step 2: Identify the application vs framework boundary Your application code has a recognisable package/path: • Java: com.yourcompany.service, com.yourcompany.api • Python: /app/, /src/yourproject/ • Node.js: /app/, /src/ — anything NOT in node_modules/ • .NET: YourCompany.YourProject
Step 3: Find the top-most application frame This is the entry point — where your code first entered the failing call chain.
Step 4: Find the bottom-most application frame This is the last line of your code before the error — closest to the crash.
When to examine library frames: • Misconfiguration errors (wrong constructor arguments) • Version compatibility issues • When the exception type is explicitly from the library (sqlalchemy.exc.DataError)
Stack trace vocabulary for reports: "The error originates in UserService.validateInput() at line 89." "The exception propagates from [library] through [your class] to [entry point]." "The root cause appears to be a null reference in the data layer, not a framework issue."