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'– auserváltozó értékeNone, és aemailattribú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_emailfüggvénybeNoneé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 auserváltozó honnan jön és miért lettNone.
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,displayProductfüggvény. - Hibaüzenet:
undefinedértéken próbáljuk elérni anametulajdonságot. Aproduct.js23. sorát kell megnézni – valószínűleg egyproductváltozóundefined, ésproduct.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$userváltozónull, ahol egyUserobjektumot várnánk. - Kérdés: honnan jön a
$user? Az adatbázisból? Esetleg afind()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:
- Logolj a hibás sor elé – mi az értéke minden érintett változónak közvetlenül a hiba előtt?
- Nézd meg a hívó oldalt – ki hívta ezt a függvényt? Milyen értékkel?
- 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. Azauth.py44. 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.jsfájl 15. során aproductsváltozó valószínűleg nem tömb (hanemnull,undefinedvagy 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
cartvá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 areturn 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:
- Melyik változó okozza a hibát?
- Mit próbál tenni a kód ezen a változón?
- Mi lehet az oka, hogy ez a változó
None?
Mintaválasz:
- A
mailing_listváltozó értékeNone(vagy amailing_list.recipientsértékeNone, ez is lehetséges – meg kell vizsgálni).- A kód iterálni próbál rajta (
for recipient in ...), deNone-on nem lehet iterálni.- Lehetséges okok: a
mailing_listnem lett visszaadva az adatbázisból (pl. nem létező ID alapján keresés), a konstruktor nem inicializálta arecipientstulajdonsá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:
- A hiba forrása:
InvoiceController.php28. során azInvoiceService->generateInvoice()metódustnullértékkel hívják, aholOrdertípust kellene kapnia.- Megnézem az
InvoiceController.php28. sorát: honnan jön az$orderváltozó? Request paraméterből? Adatbázis-lekérdezésből?- 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.- 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.