Uncaught exceptions in AngularJS can cause unexpected application crashes, leading to a poor user experience. To prevent this, you need a global exception handling mechanism that captures errors, logs them, and ensures your application remains stable.
1. Default Behavior of Uncaught Exceptions
By default, if an exception occurs inside an AngularJS controller, service, or directive, AngularJS logs it to the console but does not handle it in a structured way.
Example: Unhandled Exception in a Controller
angular.module('myApp', [])
.controller('myController', function($scope) {
$scope.triggerError = function() {
throw new Error("Something went wrong!");
};
});
If you call $scope.triggerError()
, the browser console will show:
Error: Something went wrong!
This method does not allow centralized logging, remote tracking, or user notifications.
2. Using $exceptionHandler
for Global Exception Handling
AngularJS provides the $exceptionHandler
service to catch all uncaught exceptions globally.
Customizing $exceptionHandler
Modify $exceptionHandler
inside a .config()
block using $provide.decorator
:
angular.module('myApp', [])
.config(function($provide) {
$provide.decorator('$exceptionHandler', function($delegate, $log) {
return function(exception, cause) {
// Log error to the console
$log.error("Global Exception:", exception);
// Send error to a remote server
logErrorToServer(exception, cause);
// Call the default exception handler
$delegate(exception, cause);
};
});
});
// Function to send error details to the server
function logErrorToServer(exception, cause) {
console.log("Logging error to server:", exception.message, "Cause:", cause);
}
How It Works
Logs the error using $log.error()
Sends error details to a remote server for tracking
Preserves AngularJS’s default behavior by calling $delegate(exception, cause)
3. Handling Uncaught Async Errors
AngularJS does not automatically catch errors inside asynchronous operations, such as $http
requests, $timeout
, or promises.
Example: Handling $http
Errors
angular.module('myApp', [])
.controller('myController', function($scope, $http, $exceptionHandler) {
$scope.loadData = function() {
$http.get('invalid_url')
.then(function(response) {
$scope.data = response.data;
})
.catch(function(error) {
$exceptionHandler(error, "HTTP Request Error");
});
};
});
Without .catch()
, errors won’t trigger $exceptionHandler
, so always use .catch(error => $exceptionHandler(error, "Message"))
.
4. Using $q
Exception Handling for Promises
When using promises ($q
), you can catch unhandled exceptions globally.
Example: Handling $q
Errors
angular.module('myApp', [])
.config(function($provide) {
$provide.decorator('$q', function($delegate) {
let originalReject = $delegate.reject;
$delegate.reject = function(reason) {
console.error("Unhandled Promise Rejection:", reason);
return originalReject(reason);
};
return $delegate;
});
});
Intercepts rejected promises
Logs promise-related errors
5. Handling Uncaught Errors with $window.onerror
If an error occurs outside AngularJS (e.g., inside a third-party library), $exceptionHandler
will not catch it. You need to use $window.onerror
.
Example: Capturing Global Errors
angular.module('myApp', [])
.run(function($window, $exceptionHandler) {
$window.onerror = function(message, source, lineno, colno, error) {
$exceptionHandler(error || message, "Global JavaScript Error");
};
});
Captures errors from non-Angular code
Logs errors caused by external scripts
6. Tracking Errors with User Details
For better debugging, log errors along with user session details.
Example: Logging Errors with User Info
angular.module('myApp', [])
.config(function($provide) {
$provide.decorator('$exceptionHandler', function($delegate, $log, UserService) {
return function(exception, cause) {
let user = UserService.getCurrentUser();
let errorDetails = {
message: exception.message,
stack: exception.stack,
cause: cause,
user: user
};
$log.error("User Error:", errorDetails);
logErrorToServer(errorDetails);
$delegate(exception, cause);
};
});
});
// Simulated UserService
angular.module('myApp').service('UserService', function() {
this.getCurrentUser = function() {
return { id: 101, name: "John Doe" };
};
});
Includes user context
Helps debug user-specific issues
7. Preventing Errors in Production
To disable detailed error logs in production, use $logProvider.debugEnabled(false)
.
Example: Disable Debugging Logs in Production
angular.module('myApp', [])
.config(function($logProvider) {
$logProvider.debugEnabled(false);
});
Prevents sensitive error details from being logged
Hides stack traces in production