In this blog post I will show how to set up custom error pages in ASP.NET MVC 3 applications to create user-friendly error messages instead of the (yellow) IIS default error pages for both “normal” (non-AJAX) requests and jQuery AJAX requests.

In this showcase we will implement custom error pages to handle the HTTP error codes 404 (“Not Found”) and 500 (“Internal server error”) which I think are the most common errors that could occur in web applications. In a first step we will set up the custom error pages to handle errors occurring in “normal” non-AJAX requests and in a second step we add a little JavaScript jQuery code that handles jQuery AJAX errors.

We start with a new (empty) ASP.NET MVC 3 project and activate custom errors in the Web.config by adding the following lines under <system.web>:

mode="On" defaultRedirect="/Error">
  <error redirect="/Error/NotFound" statusCode="404"/>
  <error redirect="/Error/InternalServerError" statusCode="500"/>

Note: You can set
mode=”Off” to disable custom errors which could be helpful while developing or debugging. Setting mode=”RemoteOnly” activates custom errors only for remote clients, i.e. disables custom errors when accessing via http://localhost/[...]. In this example setting mode=”On” is fine since we want to test our custom errors. You can find more information about the <customErrors> element here.

In a next step we
remove the following line in Global.asax.cs file:

filters.Add(new HandleErrorAttribute());

and add a new
ErrorController (Controllers/ErrorController.cs):

class ErrorController : Controller

  public ActionResult Index()
    return InternalServerError();

  public ActionResult NotFound()
    Response.TrySkipIisCustomErrors = true;
    Response.StatusCode = (int)HttpStatusCode.NotFound;
    return View("NotFound");

  public ActionResult InternalServerError()
    Response.TrySkipIisCustomErrors = true;
    Response.StatusCode = (int)HttpStatusCode.InternalServerError;
    return View("InternalServerError");

In a last step we add the
ErrorController‘s views (Views/Error/NotFound.cshtml and Views/Error/InternalServerError.cshtml) that defines the (error) pages the end user will see in case of an error. The views include a partial view defined in Views/Shared/Error/NotFoundInfo.cshtml respectively Views/Shared/Error/InternalServerErrorInfo.cshtml that contains the concrete error messages. As we will see below using these partial views enables us to reuse the same error messages to handle AJAX errors.



  ViewBag.Title = "Not found";




The URL you have requested was not found.


  ViewBag.Title = "Internal server error";


An internal Server error occured.

To handle errors occurring in (jQuery) AJAX calls we will use jQuery UI to show a dialog containing the error messages. In order to include jQuery UI we need to add two lines to Views/Shared/_Layout.cshtml:

<link href="@Url.Content("~/Content/themes/base/jquery.ui.all.css")" rel="stylesheet" type="text/css" />

<script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>

Moreover we add the following jQuery JavaScript code (defining the global AJAX error handling) and the Razor snippet (defining the dialog containers) to Views/Shared/_Layout.cshtml:

script type="text/javascript">
  $(function () {
    // Initialize dialogs ...
    var dialogOptions = {
      autoOpen: false,
      draggable: false,
      modal: true,
      resizable: false,
      title: "Error",
      closeOnEscape: false,
      open: function () { $(".ui-dialog-titlebar-close").hide(); }, // Hide close button
      buttons: [{
        text: "Close",
        click: function () { $(this).dialog("close"); }

    // Set up AJAX error handling ...
    $(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError)

      if (jqXHR.status == 404) {
      } else if (jqXHR.status == 500) {
      } else {
        alert("Something unexpected happend :( ...");

<div id="NotFoundInfoDialog">
  @{ Html.RenderPartial("Error/NotFoundInfo"); }
<div id="InternalServerErrorDialog">
  @{ Html.RenderPartial("Error/InternalServerErrorInfo"); }

As you can see in the Razor snippet above we reuse the error texts defined in the partial views saved in Views/Shared/Error/.

To test our custom errors we define the HomeController (Controllers/HomeController.cs) as follows:

  public class HomeController : Controller
  public ActionResult Index()
    return View();
  public ActionResult Error500()
    throw new Exception();

and the corresponding view Views/Home/Index.cshtml:

  ViewBag.Title = "ViewPage1";

<script type="text/javascript">
  $function () {
    $("a.ajax").click(function (event) {
      url: $(this).attr('href'),

  <li>@Html.ActionLink("Error 404 (Not Found)", "Error404")</li>
  <li>@Html.ActionLink("Error 404 (Not Found) [AJAX]", "Error404", new { },
new { Class = "ajax" })</li>

  <li>@Html.ActionLink("Error 500 (Internal Server Error)", "Error500")</li>
  <li>@Html.ActionLink("Error 500 (Internal Server Error) [AJAX]", "Error500", new { }, new { Class = "ajax" })</li>

To test the custom errors you can launch the project and click one of the four links defined in the view above. The “AJAX links” should open a dialog containing the error message and the “non-AJAX” links should redirect to a new page showing the same error message.

Summarized this blog post shows how to set up custom errors that handle errors occurring in both AJAX requests and “non-AJAX” requests. Depending on the project, one could customize the example code shown above to handle other HTTP errors as well or to show more customized error messages or dialogs.