modulo 02 / lezione 11
Leggere errori e stack trace
Stack trace: da dove arriva davvero il problema
Seguire la catena delle chiamate distinguendo punto in cui esplode e punto in cui nasce la causa, comprendendo l'ordine LIFO dello stack.
In parole semplici
Lo stack trace è la pila delle chiamate attive nel momento esatto dell’errore. Si legge dall’alto verso il basso: in alto la funzione dove l’errore è stato lanciato, sotto chi l’ha chiamata, sotto ancora chi ha chiamato quella, e così via. È una mappa di “come siamo arrivati qui”.
L’errore frequente del principiante: guardare solo la prima riga. La prima riga è dove esplode, non sempre dove nasce la causa.
Parole nuove
- stack (LIFO): pila in cui l’ultima funzione chiamata è la prima ad uscire (Last In, First Out).
- frame: una riga dello stack trace (una chiamata in corso).
- call site: punto dove una funzione viene chiamata.
- innermost frame: frame più in alto, dove l’errore è stato lanciato.
Cosa impari
- Leggere uno stack trace in ordine corretto.
- Distinguere il punto di esplosione dal punto in cui nasce la causa.
- Riconoscere le righe “interne” (codice tuo) da quelle “esterne” (framework, librerie, browser).
Esempio commentato
TypeError: Cannot read properties of undefined (reading 'nome')
at mostraUtente (app.js:42:18) ← punto di esplosione
at handleClick (app.js:60:5) ← chi ha chiamato mostraUtente
at HTMLButtonElement.<anonymous> ← chi ha chiamato handleClick
(app.js:75:3) (il listener registrato)
Lettura:
- Riga 42 di
app.jsprova a leggere.nomesu qualcosa che èundefined. - Stava eseguendo
mostraUtente, che è stato invocato dahandleClicka riga 60. handleClickera a sua volta un listener attaccato a unHTMLButtonElement, registrato a riga 75.
Esplosione vs causa
Lo stack ti dice dove esplode. Non sempre ti dice perché.
Esempio:
function caricaUtenti() {
return null; // ← la causa nasce qui
}
function mostraNomi() {
const utenti = caricaUtenti();
return utenti.map(u => u.nome); // ← esplode qui
}
Stack trace:
TypeError: Cannot read properties of null (reading 'map')
at mostraNomi (app.js:6:18)
L’errore parla solo di mostraNomi. Ma la causa è in caricaUtenti. Lo stack è il punto di partenza, non quello di arrivo.
Ordine LIFO: dall’innermost al chiamante
Lo stack cresce con le chiamate e si svuota con i return:
chiamata A → stack: [A]
A chiama B → stack: [A, B]
B chiama C → stack: [A, B, C]
C lancia errore → stack al momento dell'errore: [A, B, C]
Lo stack trace si stampa dall’alto (C, l’ultimo entrato) al basso (A, il primo).
Frame “tuoi” e frame “esterni”
In un’app reale, lo stack è pieno di frame di framework e librerie:
TypeError: ...
at mostraUtente (app.js:42) ← tuo
at handleClick (app.js:60) ← tuo
at HTMLButtonElement.<anonymous> ← bridge browser ↔ tuo codice
at dispatchEvent (react-dom.js:1234) ← framework
at SafariEventDispatcher (react-dom.js:888) ← framework
Concentrati sui frame tuoi. I frame del framework sono utili solo per casi specifici (bug del framework stesso, configurazioni errate).
Async stack trace (cenno)
Con async/await, lo stack non sempre mostra la catena logica. Esempio:
async function a() { return await b(); }
async function b() { return await c(); }
async function c() { throw new Error("boom"); }
a();
In runtime moderni (Chrome, Node 16+) lo stack mostra c → b → a. In runtime vecchi vedresti solo c. Approfondiremo nel modulo 7.
Source map (cenno)
In produzione il codice è minificato: i nomi diventano a, b, c, le righe si compattano. Lo stack diventa illeggibile:
TypeError: ...
at e (main.abc123.js:1:5432)
La source map (modulo 7) collega main.abc123.js:1:5432 al sorgente originale. Senza source map, lo stack di produzione è quasi inutile.
Esercizio guidato
Crea tre funzioni che si chiamano a catena:
function uno() { return due(); }
function due() { return tre(); }
function tre() { throw new Error("bersaglio"); }
uno();
- Esegui con
node. Copia lo stack trace. - Annota: chi è l’innermost frame? Chi è il più basso?
- Modifica
trein modo che usiundefined.xinvece di lanciare un Error. Confronta gli stack. - Aggiungi un quarto livello che chiama
unoda unsetTimeout. Osserva come cambia lo stack.
Errori frequenti del principiante
- Fermarsi alla prima riga. Spesso il vero indizio è due righe sotto.
- Confondere “esplosione” e “causa”. Lo stack ti dice dove, non perché.
- Ignorare i frame propri perché sembrano “ovvi”. Il frame ovvio è spesso quello sbagliato.
- Buttare via stack di produzione perché illeggibili. Carica le source map e riprova.