Fatal Error è un errore critico che causa la terminazione immediata di un'applicazione (crash). A differenza di un errore non fatale, l'errore fatale non lascia al programma alcuna possibilità di recupero — il processo viene terminato forzatamente dal sistema operativo o dall'ambiente di runtime. Secondo Firebase Crashlytics 2024, l'app media perde il 2,5% degli utenti dopo ogni crash e la correzione degli errori fatali è la priorità numero uno nello sviluppo mobile. Più alto è il tasso di crash-free, più alta è la valutazione dell'app negli store e minore è la perdita di utenti.
Punti chiave
Fatal Error è un errore per cui l'esecuzione del programma non può continuare. Il sistema operativo o la macchina virtuale termina il processo per prevenire la corruzione dei dati. In iOS, un errore fatale attiva un segnale SIGABRT o SIGSEGV; in Android, un'eccezione non gestita che raggiunge il gestore radice e termina il processo. L'app si chiude istantaneamente e l'utente torna alla schermata Home.
I segni caratteristici di un errore fatale: un crash report con stack trace completo, scomparsa inaspettata dell'app, una voce nel log di sistema sulla terminazione del processo, schermo nero o bianco prima della chiusura. L'utente vede la schermata Home senza alcun modo di recuperare la sessione — l'app deve essere riavviata da capo. In iOS, un crash è accompagnato da un file .crash accessibile tramite Xcode Organizer.
Ogni crash influisce negativamente sulla fidelizzazione degli utenti. Secondo Google Play Console 2024, le app con un tasso di crash-free inferiore al 99,5% ricevono valutazioni più basse nella ricerca e nei suggerimenti. Il tasso di crash è uno dei principali segnali di qualità per App Store e Google Play — un alto livello di errori fatali può bloccare la pubblicazione degli aggiornamenti. Per le applicazioni finanziarie e mediche, un tasso di crash-free inferiore al 99,9% è considerato inaccettabile.
Dereferenziamento di puntatore nullo è la causa principale degli errori fatali nelle applicazioni mobili. Tentare di accedere a una proprietà o metodo di un oggetto che è null causa NullPointerException in Android o EXC_BAD_ACCESS in iOS. Secondo JetBrains 2023, circa il 28% di tutti i crash in produzione sono correlati a puntatori nulli. Il sistema di null-safety di Kotlin riduce significativamente questa percentuale, ma force unwrap e la compatibilità Java rimangono fonti del problema.
Accedere a un elemento di una collezione con un indice inesistente è la seconda causa più comune di crash. In Java e Kotlin è ArrayIndexOutOfBoundsException; in Swift — fatal error: Index out of range. Si verifica più spesso quando si lavora con liste dopo il filtraggio o la modifica dinamica delle dimensioni della collezione. L'uso di metodi sicuri come getOrNull (Kotlin) o indices.contains (Swift) previene questo tipo di errore fatale.
Mancanza di memoria (OutOfMemoryError), overflow dello stack (StackOverflowError), caricamento di una risorsa inesistente — gli errori di risorse sono spesso fatali e difficili da riprodurre. OutOfMemoryError si verifica durante il caricamento di immagini grandi senza compressione o a causa di perdite di memoria da riferimenti non rilasciati. StackOverflowError si verifica con ricorsione profonda senza un caso base o con chiamate cicliche in una catena di delegati.
Deadlock, race condition, modifica di una collezione durante l'iterazione — errori di multithreading si manifestano in modo non deterministico e sono i più difficili da diagnosticare. In Android, ConcurrentModificationException durante la modifica di un ArrayList da thread diversi; in iOS, crash durante la modifica di un NSMutableArray senza sincronizzazione. L'uso di coroutine Kotlin (structured concurrency) o Swift Actors (iOS 16+) riduce la probabilità di crash di concorrenza.
La differenza chiave è la recuperabilità. Non-Fatal Error permette al programma di continuare: un timeout di rete viene gestito con try-catch, un errore di parsing viene sostituito con un valore predefinito. Un Fatal Error non ha questa via — il crash è inevitabile e l'app deve essere riavviata. Il confine tra questi tipi di errore è determinato dall'architettura dell'applicazione.
| Caratteristica | Fatal Error | Non-Fatal Error |
|---|---|---|
| Terminazione app | Sì | No |
| Recupero | Impossibile | Possibile tramite catch |
| Raccolta informazioni | Solo crash reporter | Registrazione dal codice |
| Danno UX | Fallimento completo della sessione | Disagio temporaneo |
| Esempio tipico | NullPointerException | IOException |
Lo stesso errore può essere fatale su una piattaforma e non fatale su un'altra. Divisione per zero in Java/Kotlin lancia ArithmeticException (non fatale — può essere catturata), mentre in Swift causa fatal error: Division by zero (un crash senza possibilità di cattura). Lo sviluppatore deve considerare il comportamento del linguaggio e dell'ambiente di runtime specifici quando progetta la gestione degli errori. Comprendere il confine tra fatale e non fatale è la base per costruire un'architettura tollerante ai guasti nelle applicazioni mobili.
Firebase Crashlytics è lo standard de facto per diagnosticare i crash nelle applicazioni mobili. L'SDK raccoglie automaticamente stack trace, stato del dispositivo, versione del SO e log immediatamente prima del crash. La dashboard raggruppa i crash identici in un unico issue, mostrando il numero di utenti interessati, la frequenza e la versione dell'app in cui si è verificato il crash.
// Inizializzazione di Crashlytics in un'applicazione Android
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
Crashlytics.setCustomKey("build_type", "production")
}
}
// Impostazione dei dati utente personalizzati per la diagnostica dei crash
Crashlytics.setUserId("user_12345")
Crashlytics.setCustomKey("screen", "ProfileFragment")
Crashlytics.setCustomKey("api_response", responseCode)
// Crash forzato per testare l'integrazione
Crashlytics.crash()
Sentry è un'alternativa con una diagnostica più dettagliata. Sentry mostra non solo lo stack trace, ma anche lo stato di tutte le variabili, la sequenza degli eventi prima dell'errore e il contesto di esecuzione. Breadcrumbs di Sentry consentono di ricostruire la catena di azioni dell'utente prima dell'errore fatale: clic sui pulsanti, transizioni tra schermate, richieste di rete. Sentry offre anche monitoraggio delle prestazioni e delle sessioni per un'analisi completa della qualità.
Per una corretta diagnosi dei crash su iOS, i file dSYM (simboli di debug) devono essere caricati in Crashlytics o Sentry. Senza dSYM, lo stack trace conterrà solo indirizzi di memoria invece di nomi di funzioni. Per Android, i file di mapping devono essere caricati quando si utilizza ProGuard o R8. L'automazione del caricamento dSYM tramite una build phase in Xcode o un plugin Gradle è obbligatoria per le build di produzione.
Il metodo di prevenzione di base è il safe unwrapping di tutti i valori opzionali e nullable. L'uso di if-let in Swift e let con ?: in Kotlin elimina gli errori di puntatore nullo. Nessun force unwrap senza garanzia di un valore. Sia il compilatore Kotlin che Swift avvertono sulle operazioni potenzialmente pericolose — questi avvisi non possono essere ignorati nel codice di produzione.
// PREVENIRE il fatal error attraverso safe unwrapping
func processUser(id: String) -> String {
guard let user = database.findUser(by: id) else {
return "User not found"
}
guard let email = user.email else {
return "Email not set"
}
return email
}
// Accesso sicuro agli elementi della collezione
func safeGet <T>(items: [T], index: Int) -> T? {
guard items.indices.contains(index) else { return nil }
return items[index]
}
// Verifica dei limiti dell'array prima dell'accesso
let numbers = [1, 2, 3]
if numbers.indices.contains(5) {
print(numbers[5])
} else {
print("Index out of range")
}
Defensive programming è il secondo livello di protezione. Controlla sempre i parametri di input delle funzioni, restituisci Optional o Result invece di force unwrap e usa assert nelle build di debug per il rilevamento precoce degli errori durante lo sviluppo. Test unitari per casi limite (null, collezioni vuote, indici non validi) dovrebbero coprire tutti i punti di ingresso pubblici nella logica di business dell'applicazione.
In React Native e SwiftUI, puoi impostare un error boundary — un componente che cattura errori fatali di rendering e mostra un'UI di fallback invece di un crash. Questo trasforma un errore UI fatale in non fatale dal punto di vista dell'utente — l'app continua a funzionare e l'utente vede un messaggio di errore in un blocco di interfaccia specifico invece di uno schermo bianco.
Integrazione di controlli automatici nella pipeline CI/CD: analisi statica (Detekt per Kotlin, SwiftLint per Swift), esecuzione di test UI su dispositivi reali, verifica del tasso di crash-free nell'ambiente di test. Blocco dei merge quando viene superata la soglia del tasso di crash (soglia raccomandata: più dello 0,1% di nuovi crash per commit).
Domande frequenti
No, dopo un fatal error il recupero è impossibile — il processo termina a livello di OS. L'unico modo è prevenire l'errore fatale prima che si verifichi attraverso costruzioni sicure, defensive programming e test approfonditi dei casi limite durante lo sviluppo.
Segfault (SIGSEGV) è un tipo di errore fatale che si verifica quando si accede a un'area di memoria non valida. FATAL ERROR è un termine generale per tutti gli errori irreversibili, inclusi segfault, abort, stack overflow, out of memory ed eccezioni non gestite in runtime.
L'integrazione dell'SDK di Crashlytics (Firebase) o Sentry raccoglie automaticamente tutte le eccezioni non gestite. L'SDK intercetta i segnali del SO e le eccezioni runtime, genera un crash report con stack trace e contesto e lo invia al server al successivo avvio dell'app.
Per testare la gestione dei crash, si utilizza un force crash in una build di debug. Crashlytics fornisce il metodo crash() per simulare un errore fatale. I test unitari verificano la correttezza di guard e if-let, mentre i test UI coprono i casi limite di input dei dati e stati dell'interfaccia.
No, solo le eccezioni non gestite diventano fatali. Un'eccezione catturata da try-catch è non fatale. La differenza tra un'eccezione gestita e non gestita determina se l'app terminerà o continuerà a funzionare con uno stato alternativo con un danno minimo all'esperienza utente.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche