
TypeError: Failed to fetch è il browser che dice che una chiamata fetch() non è mai arrivata a
compimento. Non che sia fallita - che non è mai andata abbastanza avanti da avere un risultato.
Questa distinzione conta più di qualsiasi altra cosa in questa pagina, perché esclude quasi tutto
quello che si sospetta per primo. Un 404 non produce questo errore. Nemmeno un 500. Quelle sono
richieste completate: il server ha risposto, fetch() si risolve e response.ok è false. Failed
to fetch significa che non c’è alcun oggetto risposta da ispezionare, ed è esattamente per questo
che il messaggio ti dice così poco.
Chrome, Firefox e Safari lo formulano ciascuno a modo suo - Failed to fetch, NetworkError when
attempting to fetch resource, Load failed - e intendono la stessa cosa.
Le cause, nell’ordine in cui capitano davvero
CORS. La più comune di gran lunga. La richiesta è partita, il server ha risposto e il browser si è rifiutato di consegnare la risposta al tuo codice perché le intestazioni non lo permettevano. La console stampa un messaggio a parte sulla politica delle origini, e il fetch viene rifiutato con l’errore generico. Se leggi solo il rifiuto non impari niente; la riga sopra è la risposta vera.
La connessione non è mai avvenuta. Offline, errore DNS, host irraggiungibile, server che non è in ascolto. È lo stesso terreno della famiglia ERR_CONNECTION, e il pannello di rete lo nomina.
Qualcosa l’ha bloccata. Un blocco della pubblicità, un’estensione per la privacy, un proxy aziendale. Le richieste verso qualsiasi cosa somigli ad analytics o tracciamento vengono respinte prima ancora di lasciare il browser, e il pannello di rete le segna come bloccate. Questa è invisibile sulla tua macchina se sei l’unico senza quell’estensione.
Contenuto misto. Una pagina https che chiede un URL http. Il browser lo blocca senza
appello.
La richiesta è stata annullata. La pagina è andata altrove, un componente è stato smontato, è
scattato un AbortController, la scheda è stata chiusa a metà richiesta. Cosa ordinaria in
un’applicazione a pagina singola, e nel tracciamento degli errori si presenta come una valanga di
fallimenti che non sono capitati a nessuno.
Un URL sbagliato. Un refuso, un percorso relativo che risolve dove non doveva, una variabile
d’ambiente vuota al momento della build, così la richiesta è finita su undefined/api/thing.
Failed to fetch dynamically imported module
Una variante specifica che merita un paragrafo suo, perché la causa non è nel tuo codice e nemmeno la soluzione.
Un’applicazione costruita con il code splitting carica i chunk con nomi di file che contengono un hash. Fai il deploy, gli hash cambiano, i vecchi file smettono di esistere. Chiunque abbia già la pagina aperta - una scheda lasciata aperta tutta la notte, un telefono che non ha mai ricaricato - chiede un nome di file che ora è un 404, e riceve questo errore nel momento in cui va su una rotta il cui chunk non è ancora stato caricato.
Due rimedi, e lavorano insieme. Tieni ancora un po’ gli asset della build precedente invece di sostituirli, così le schede aperte continuano a funzionare. E intercetta il fallimento nel router: se un import dinamico viene rifiutato, proponi un ricaricamento, perché un ricaricamento lo risolve davvero.
Come capire quale delle cause hai
La console non te lo dirà. Il pannello di rete sì, e ci vogliono una decina di secondi.
Aprilo, riproduci il fallimento e trova la richiesta in rosso. Poi leggi tre colonne:
-
Stato. Vuoto o
(failed)vuol dire che la richiesta non è mai arrivata a compimento.(blocked:...)dice che cosa l’ha bloccata. - L’host. Il tuo dominio, oppure quello di qualcun altro. Un fallimento verso una terza parte è un problema diverso da un fallimento verso la tua API.
- Il messaggio di console subito sopra il rifiuto. Se parla di un’origine o di un preflight, hai un problema di CORS e tutto il resto di questa pagina è una distrazione.
Se la richiesta non compare affatto nel pannello di rete, non ha mai lasciato il browser: contenuto misto, un’estensione, o un URL che non è mai stato valido.
Session Replay
Estensione gratuita per Chrome. Un clic sulla pagina che si comporta male cattura lo screenshot, la console e il registro di rete, e ti restituisce un link da incollare nel ticket.
Perché è l’errore peggiore da ricevere di seconda mano
Tutto quanto sopra presuppone che tu riesca a riprodurlo. Il più delle volte non ci riesci, perché le cause che producono questo errore sono proprio quelle che vivono sulla macchina di qualcun altro: la sua estensione, la sua rete, il suo proxy aziendale, la sua scheda vecchia.
Così arriva come “la pagina dice solo che qualcosa è andato storto”, e il messaggio di console che
forse ti riferiranno, Failed to fetch, è l’unico messaggio della pagina che non porta alcuna
informazione. La prova che chiuderebbe la questione è una riga in un pannello di rete che nessuno
sta guardando.
È questo l’argomento per catturare il registro di rete nel momento in cui succede, invece di fare domande dopo. Un file HAR è il modo manuale di farlo. In ogni caso, quello che risolve questa classe di bug è la riga, non la frase.
In breve
- Significa che la richiesta non è mai arrivata a compimento. Nessuna risposta, nessun codice di stato, niente da ispezionare.
- Un 404 o un 500 non lo causano - quelli sono viaggi di andata e ritorno riusciti con risposte spiacevoli.
- Guarda prima la riga di console sopra il rifiuto. Se parla di CORS, quella è la tua risposta.
- Poi controlla se la richiesta compare almeno nel pannello di rete. Se non c’è, qualcosa nel browser l’ha fermata prima che partisse.
- Dopo un deploy, “failed to fetch dynamically imported module” vuol dire che una vecchia scheda sta chiedendo un file che la tua ultima build ha cancellato.