6.2 Stack trace olvasása

← 6. Debugging gondolkodásmód

6.2 Stack trace olvasása – ahol a legtöbb junior megáll

Áttekintő

Hibaüzenet jelenik meg. Húsz sor szöveg. Ismeretlen fájlnevek, ismeretlen osztályok, framework-belső hívások tömege. A legtöbb junior ilyenkor vagy pánikba esik, vagy copy-paste-eli az egészet a Google-be – és egyik sem hatékony megközelítés.

A stack trace nem ellenség. Ellenkezőleg: ez a legprecízebb hibajelentés, amit egy program adhat – megmondja, hogy pontosan mi romlott el, és hogyan jutott oda a program. Csak meg kell tanulni olvasni.

Ez az alfejezet megtanítja, hogyan navigálj egy stack trace-ben – bármilyen programozási nyelvben. A példák Python, JavaScript és PHP kódból érkeznek, de a gondolkodásmód mindenhol ugyanaz.


Részletes leírás

Mi a stack trace és miért keletkezik?

Amikor egy program hibát dob, a futtatókörnyezet rögzíti a hívási veremet (call stack): azt a listát, hogy melyik függvény hívta melyiket, egészen addig a pontig, ahol a hiba bekövetkezett.

A stack trace ennek a veremnek a kiíratása. Visszafelé olvasható: a legfelső sor az, ahol a hiba ténylegesen bekövetkezett, az alatta lévők az ahhoz vezető hívási lánc.

Hívási verem (futás közben):
  fő program
    └── login()
          └── validate_user()
                └── query_database()   ← itt dobta a hibát

Ha a query_database() hibát dob, a stack trace fordítva írja ki: a hiba helye van legfelül, a hívási lánc alul.


A stack trace anatómiája

Minden stack trace-nek van három fő alkotóeleme:

1. A hibaüzenet – a legfontosabb sor, általában a legelején vagy legvégén.

2. A fájl/sor hivatkozások – minden egyes hívási szintnél megjelenik a fájlnév és a sorszám, ahol a hívás történt.

3. A keretrendszer sorai – ezek a framework vagy a standard könyvtár belső hívásai. Ezek foglalják el a legtöbb helyet, és legtöbbször nem ezeket kell megjavítanod.


Hogyan olvasd? – A két irány

Felülről olvasva: megtalálod a konkrét hibaüzenetet és a legközelebbi hibaforrást.

Alulról olvasva: megérted, hogyan jutott oda a program – mi hívta a hibás kódot.

A legtöbb esetben az a célod, hogy megtaláld a saját kódod legfelső sorát a stack trace-ben. Ez az a pont, ahol a te kódodban valami rosszul ment – a többi sor általában csak a keretrendszer belső mechanizmusa.


Saját kód vs. framework kód – hogyan ismerd fel

A framework sorai általában:

  • Ismeretlen könyvtárak belső fájljaira mutatnak (pl. vendor/, node_modules/, site-packages/)
  • Generált vagy belső névtereket tartalmaznak
  • Nem a te projekted mappájára hivatkoznak

A saját kódod sorai:

  • A te projekt mappájára mutatnak (pl. app/, src/, a projekt gyökere)
  • Ismerős fájlneveket tartalmaznak
  • Azokat a sorokat jelölik, ahol te írtál kódot

Praktikus szabály: keresd a legfelső sort a stack trace-ben, ami a saját projekted mappájára mutat. Ez a legtöbb esetben a hibád forrása – vagy ahhoz nagyon közel van.


Tipikus hibaüzenetek és mit jeleznek

NullPointerException / AttributeError / TypeError (null / undefined hozzáférés)

Ez a leggyakoribb hiba. Azt jelenti: egy változó, amit valamilyen értékkel kellett volna feltölteni, valójában null, None, undefined – és te valamit próbálsz rajta csinálni (elérni egy tulajdonságát, meghívni egy metódusát).

Python: AttributeError: 'NoneType' object has no attribute 'email'
JavaScript: TypeError: Cannot read properties of null (reading 'email')
PHP: Call to a member function email() on null

Mit keress: melyik változó null/None? Miért lett az? Nem adták vissza az adatbázisból? Nem lett inicializálva? Rossz feltétel mentén fut a kód?

IndexError / ArrayIndexOutOfBoundsException (tömbhivatkozás hibák)

Python: IndexError: list index out of range
JavaScript: (nem dob hibát, hanem undefined-ot ad vissza – ez is csapda)
PHP: Notice: Undefined offset: 3

Mit keress: melyik tömbön, melyik indexen? Üres a tömb? Rövidebb, mint vártad? Az index dinamikusan számított, és rossz értéket kapott?

TypeError (típushibák)

Python: TypeError: unsupported operand type(s) for +: 'int' and 'str'
JavaScript: TypeError: arr.map is not a function
PHP: TypeError: Argument #1 ($user) must be of type User, null given

Mit keress: milyen típust várt a kód, és milyet kapott? Honnan jött a rossz típusú érték?

SyntaxError / ParseError

Python: SyntaxError: invalid syntax
JavaScript: SyntaxError: Unexpected token '}'
PHP: Parse error: syntax error, unexpected token "}"

Mit keress: a hiba sora általában pontosan megmondja. Ezek a legkönnyebb hibák – nézd meg azt a sort és a közvetlenül előtte lévőt (a tényleges szintaxishiba olykor egy sorral feljebb van).


Három lépéses módszer stack trace olvasásához

1. lépés: Olvasd el a hibaüzenetet. Mi a típusa (AttributeError, TypeError, stb.)? Mit mond szövegesen? Sokszor elegendő ahhoz, hogy már tudd, mi a baj.

2. lépés: Keresd meg a saját kódod legfelső sorát. Menj végig a stack trace-en felülről lefelé. Az első sor, ami a te projekted fájljára mutat – ez az a pont, ahol a te kódod érintett.

3. lépés: Nézd meg azt a sort a kódban. Nyisd meg a fájlt, menj a megjelölt sorhoz. Értsd meg: mit próbált ott tenni a kód, és miért nem sikerülhetett a hibaüzenet alapján?


Életszerű példák

Python stack trace – AttributeError

A következő hiba jelenik meg a terminálban:

Traceback (most recent call last):
  File "/home/app/run.py", line 12, in <module>
    start_app()
  File "/home/app/app.py", line 34, in start_app
    user_service.send_welcome_email(user)
  File "/home/app/services/user_service.py", line 89, in send_welcome_email
    message = f"Hello, {user.email}"
AttributeError: 'NoneType' object has no attribute 'email'

Mit olvasunk ki?

  • Hibaüzenet: AttributeError: 'NoneType' object has no attribute 'email' – a user változó értéke None, és a email attribútumát próbáljuk elérni.
  • A saját kódunk legfelső sora: user_service.py, 89. sor – message = f"Hello, {user.email}"
  • Következtetés: a send_welcome_email függvénybe None értékű user érkezett. Vissza kell menni: ki hívta ezt a függvényt? Az app.py 34. sora – ott kell megnézni, hogy a user változó honnan jön és miért lett None.

Nem az a hibás sor, ahol az AttributeError keletkezett – hanem valahol korábban: ahol a user None értéket kapott ahelyett, hogy egy valódi felhasználót tartalmazott volna.


JavaScript stack trace – TypeError

TypeError: Cannot read properties of undefined (reading 'name')
    at displayProduct (product.js:23:30)
    at renderCart (cart.js:47:5)
    at App.render (App.js:12:3)
    at commitRootImpl (react-dom.development.js:26990:14)
    at commitRoot (react-dom.development.js:26855:5)
    [... további React belső sorok ...]

Mit olvasunk ki?

  • A React belső sorait (react-dom.development.js) figyelmen kívül hagyjuk.
  • A saját kódunk legfelső érintett sora: product.js, 23. sor, displayProduct függvény.
  • Hibaüzenet: undefined értéken próbáljuk elérni a name tulajdonságot. A product.js 23. sorát kell megnézni – valószínűleg egy product változó undefined, és product.name-t próbálunk olvasni.
  • Miért? Valószínűleg a kosárban egy termék nem töltődött be rendesen, vagy a termék lista egy hiányzó elemet tartalmaz.

PHP stack trace – metódushívás null-on

PHP Fatal error: Uncaught Error: Call to a member function getEmail() on null
  in /var/www/html/app/Http/Controllers/UserController.php:58
Stack trace:
#0 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(46): App\Http\Controllers\UserController->sendConfirmation()
#1 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php(260): Illuminate\Routing\ControllerDispatcher->dispatch(...)
#2 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(724): Illuminate\Routing\Route->run()
[... további Laravel belső sorok ...]

Mit olvasunk ki?

  • A vendor/ mappa sorainak nagy részét figyelmen kívül hagyjuk – ezek Laravel belső sorok.
  • Saját kódunk: UserController.php, 58. sor, sendConfirmation() metódus.
  • Hibaüzenet: null értéken hívjuk a getEmail() metódust. Valószínűleg egy $user változó null, ahol egy User objektumot várnánk.
  • Kérdés: honnan jön a $user? Az adatbázisból? Esetleg a find() null-t adott vissza, mert nem létezik ilyen rekord?

A "megtaláltam, de nem értem" helyzet

Néha rátalálsz a sorra, de nem érted, miért hibás. Ilyenkor segít:

  1. Logolj a hibás sor elé – mi az értéke minden érintett változónak közvetlenül a hiba előtt?
  2. Nézd meg a hívó oldalt – ki hívta ezt a függvényt? Milyen értékkel?
  3. Olvasd a hibaüzenetet szó szerint – a legtöbbször pontosan megmondja, mi a baj.

Tesztfeladatok

1. feladat – Stack trace irányok

Melyik állítás helyes a stack trace olvasásáról?

A) A legalsó sort kell először megnézni, mert az tartalmazza a tényleges hibát. B) A legfelső sort kell először megnézni, mert az a hibaüzenetet és a legközelebbi hibaforrást tartalmazza. C) A középső sort kell megnézni, mert a framework belső hívásai általában ott vannak. D) A sorrendjüknek nincs jelentősége.

Helyes válasz: B


2. feladat – Saját kód azonosítása

Az alábbi Python stack trace-ben melyik sor a saját kódunk legfelső érintett sora?

Traceback (most recent call last):
  File "/home/project/main.py", line 8, in <module>
    app.run()
  File "/usr/lib/python3.10/site-packages/flask/app.py", line 1190, in run
    run_simple(t.cast(str, host), port, self, **options)
  File "/usr/lib/python3.10/site-packages/werkzeug/serving.py", line 1097, in run_simple
    inner()
  File "/home/project/routes/auth.py", line 44, in login
    token = auth_service.generate_token(user)
  File "/home/project/services/auth_service.py", line 22, in generate_token
    payload = {"sub": user.id, "exp": expires}
AttributeError: 'NoneType' object has no attribute 'id'

A) flask/app.py, 1190. sor B) main.py, 8. sor C) auth.py, 44. sor D) auth_service.py, 22. sor

Helyes válasz: D – ez a legfelső sor, ahol a saját kódunk van (/home/project/ mappában), és ahol a hiba ténylegesen bekövetkezett. Az auth.py 44. sora szintén saját kód, de a végrehajtás már lejjebb jutott.


3. feladat – Hibaüzenet értelmezése

Mit jelent ez a hibaüzenet?

TypeError: Cannot read properties of undefined (reading 'length')

A) Egy tömb hosszát próbáljuk olvasni, de a tömb üres. B) Egy undefined értékű változó length tulajdonságát próbáljuk olvasni – a változó nem létezik vagy nem lett inicializálva. C) A length függvény nem létezik ebben a programozási nyelvben. D) A tömb hossza negatív érték, ami érvénytelen.

Helyes válasz: B


4. feladat – Mit keresünk?

Az alábbi PHP hiba esetén mi a legelső lépés?

PHP Fatal error: Uncaught Error: Call to a member function getUsername() on null
  in /var/www/app/Controllers/ProfileController.php:34

A) Megnézem a getUsername() metódus implementációját. B) Megnézem a ProfileController.php 34. sorát, és megállapítom, melyik változón hívom a getUsername()-t. C) Keresem a Laravel dokumentációban, hogyan kell metódust null-on hívni. D) Újraindítom az alkalmazást, hátha elmúlik magától.

Helyes válasz: B – a hibás sor azonosítása az első lépés. Ott kell megnézni, melyik változó null, és miért.


5. feladat – Framework sorok kiszűrése

Az alábbi JavaScript stack trace-ben melyik az a sor, amit meg kell vizsgálnod?

TypeError: products.filter is not a function
    at filterByCategory (utils/productUtils.js:15:28)
    at ProductList (components/ProductList.jsx:42:18)
    at renderWithHooks (react-dom.development.js:14985:18)
    at mountIndeterminateComponent (react-dom.development.js:17811:13)
    at beginWork (react-dom.development.js:19049:16)

A) react-dom.development.js, 14985. sor B) productUtils.js, 15. sor C) react-dom.development.js, 19049. sor D) ProductList.jsx, 42. sor

Helyes válasz: B – ez a legfelső sor a saját projektből. A productUtils.js fájl 15. során a products változó valószínűleg nem tömb (hanem null, undefined vagy más típus), ezért nem elérhető rajta a .filter() metódus.


6. feladat – Null értékek nyomkövetése

Az alábbi Python stack trace-t kapod:

Traceback (most recent call last):
  File "/app/api/orders.py", line 67, in create_order
    total = cart.calculate_total()
AttributeError: 'NoneType' object has no attribute 'calculate_total'

A cart.calculate_total() sort nézed meg, de a kód logikusnak tűnik. Mi a következő lépés?

A) Törölöd a calculate_total() metódust, mert biztosan hibás. B) Megkeresed, honnan jön a cart változó, és miért lett None ahelyett, hogy egy Cart objektumot tartalmazott volna. C) Hozzáadsz egy try-catch blokkot és elnyeled a hibát. D) Azt feltételezed, hogy az adatbázis hibás, és újraindítod a szervert.

Helyes válasz: B – a hiba ott van a stack trace-ben, ahol a hiba következménye megjelenik. Az ok általában korábban van: valahol a cart változó None értéket kapott ahelyett, hogy feltöltötték volna.


7. feladat – Szintaxishiba helye

PHP Parse error: syntax error, unexpected token "}" in /app/services/PaymentService.php on line 93

Az alábbi kódrészlet a PaymentService.php fájlból (84–93. sorok):

public function processPayment(array $data): bool
{
    if (empty($data['amount'])) {
        return false;
    
    $amount = (float) $data['amount'];
    $currency = $data['currency'] ?? 'HUF';
    
    return $this->gateway->charge($amount, $currency);
}

Mi a hiba és hol van?

A) A 93. sorban felesleges a } zárójel. B) A 87. sorban hiányzik a } az if blokk lezárásához, ezért a parser a 93. }-t nem tudja értelmezni. C) A (float) cast szintaxisa helytelen PHP-ban. D) A ?? operátor nem létezik PHP-ban.

Helyes válasz: B – a if (empty($data['amount'])) { blokk nem lett lezárva }-vel a return false; után. Ezért a parser a függvény végén lévő }-t nem tudja értelmezni helyesen.


8. feladat – Több saját kód sor esetén

Az alábbi stack trace-ben melyik sort vizsgálod meg először?

Error: Cannot set properties of undefined (setting 'status')
    at updateOrderStatus (api/orders.js:112:18)
    at processCheckout (checkout/CheckoutService.js:67:5)
    at handleSubmit (components/CheckoutForm.jsx:34:3)
    at HTMLFormElement.<anonymous> (react-dom.development.js:1234:12)

A) CheckoutForm.jsx, 34. sor – mert ez az UI komponens. B) CheckoutService.js, 67. sor – mert ez a fő logika. C) orders.js, 112. sor – mert ez a legfelső saját kód sor, ahol a hiba bekövetkezett. D) react-dom.development.js, 1234. sor – mert ez az első sor a listában.

Helyes válasz: C – mindig a legfelső saját kód sorral kezdünk, ahol a hiba ténylegesen keletkezett. Az alatta lévők csak a hívási lánc.


9. feladat – Összetett elemzés

Olvasd el az alábbi Python stack trace-t, és válaszolj a kérdésekre!

Traceback (most recent call last):
  File "/home/app/tasks/email_sender.py", line 18, in send_batch
    for recipient in mailing_list.recipients:
TypeError: 'NoneType' object is not iterable

Kérdések:

  1. Melyik változó okozza a hibát?
  2. Mit próbál tenni a kód ezen a változón?
  3. Mi lehet az oka, hogy ez a változó None?

Mintaválasz:

  1. A mailing_list változó értéke None (vagy a mailing_list.recipients értéke None, ez is lehetséges – meg kell vizsgálni).
  2. A kód iterálni próbál rajta (for recipient in ...), de None-on nem lehet iterálni.
  3. Lehetséges okok: a mailing_list nem lett visszaadva az adatbázisból (pl. nem létező ID alapján keresés), a konstruktor nem inicializálta a recipients tulajdonságot, vagy az objektum létrehozása korábban meghiúsult.

10. feladat – Hibakeresési terv

Az alábbi stack trace alapján írj egy rövid (3-4 lépéses) debugging tervet!

PHP Fatal error: Uncaught TypeError: Argument #1 ($order) must be of type Order, null given
  in /var/www/app/Services/InvoiceService.php:55
Stack trace:
#0 /var/www/app/Http/Controllers/InvoiceController.php(28): App\Services\InvoiceService->generateInvoice(null)
#1 /var/www/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\Http\Controllers\InvoiceController->create()

Mintaválasz:

  1. A hiba forrása: InvoiceController.php 28. során az InvoiceService->generateInvoice() metódust null értékkel hívják, ahol Order típust kellene kapnia.
  2. Megnézem az InvoiceController.php 28. sorát: honnan jön az $order változó? Request paraméterből? Adatbázis-lekérdezésből?
  3. Ha adatbázisból: Megnézem, hogy az Order::find($id) vagy hasonló hívás mit ad vissza. Ha nem létező ID-ra keres, null-t ad vissza.
  4. Javítás iránya: validálni kell a lekérdezés eredményét mielőtt átadjuk a service-nek – ha az order nem létezik, 404-es hibát kell visszaadni.
Scroll to Top