Customize Error pages on AEM Author

AEM comes with a standard error page handling, and error handling in AEM is one of the most basic task. But 99% error handling is done for AEM sites, which we also call published/live pages. In order to handle error for sites there are multiple approaches like ACS commons Error Page Handler or one can handle the error at dispatcher level itself and can leverage dispatcher caching.
But recently i came across below use case.

Use Case:- Customer wants to handle error for out of the box pages in AEM author like asset detail page. For example, User profile is not properly set up for first time login and when user does not have permission to content/dam a 404 page with stack trace is shown to user or an existing user is trying to access a resource that is not present or he/she does not has permission to access it.

Why we should handle error scenarios :-

  • We should never present excessive information to user, with stack trace printed on screen , we are exposing our repo structure.
  • It never gives good product experience , specifically to a user who is logging in for the first time.
  • Presenting a custom error page, provide a better experience and custom user message that states what needs to be done next to access the page.

The customisation that i am going to implement is generic and can be used by both sites and asset implementation. It is not just restricted to this specific use case. In this tutorial we are going to customize out of the box error handler jsp.

Customizing 404.jsp :-

To handle errors AEM provides 404.jsp under libs/sling/servlet/errorhandler. We are going to overlay this script and modify according to our requirements.

Note:- Before making any edits to 404.jsp please understand that 404.jsp script has been specifically designed to cater to AEM authentication; in particular, to allow for system login in the case of these errors. Therefore, replacement of this script should be done with great care.

Let’s try accessing a page that doesn’t exist in AEM. For example:- http://localhost:4502/content/we-retail/us/en/boy.html

If we present our customers this stack trace, it definitely doesn’t looks good.

So, let’s get started to fix this issue :-

  • Open crx/de and Navigate to /libs/sling/servlet/errorhandler.
  • Right click errorhandler folder and select Overlay Node.
  • Make sure to select Match Node Types checkbox.
overlay 404 jsp aem
  • Copy the default script 404.jsp from /libs/sling/servlet/errorhandler/ to /apps/sling/servlet/errorhandler/.
  • Navigate to /apps/sling/servlet/errorhandler and open 404.jsp.
  • Replace its code with below code. Here we are checking for specific error code and adding our custom logic.
  • To display our custom error message it is always preferred to add it in a separate file, so that it is always easy to merge/update this file during upgrades.
  Copyright 1997-2008 Day Management AG
  Barfuesserplatz 6, 4001 Basel, Switzerland
  All Rights Reserved.

  This software is the confidential and proprietary information of
  Day Management AG, ("Confidential Information"). You shall not
  disclose such Confidential Information and shall use it only in
  accordance with the terms of the license agreement you entered into
  with Day.


  Generic 404 error handler

  Important note:  
  Since Sling uses the user from the request (depending on the authentication
  handler but typically HTTP basic auth) to login to the repository and JCR/CRX
  will simply say "resource not found" if the user does not have a right to
  access a certain node, everything ends up in this 404 handler, both access
  denied ("401", eg. for non-logged in, anonymous users) and really-not-existing
  scenarios ("404", eg. logged in, but does not exist in repository).
%><%@ page session="false" %><%
%><%@taglib prefix="sling" uri="" %><%
%><sling:defineObjects /><%
%><%@ page import=",," %><%!

    private boolean isAnonymousUser(HttpServletRequest request) {
        return request.getAuthType() == null
            || request.getRemoteUser() == null;

    private boolean isBrowserRequest(HttpServletRequest request) {
        // check if user agent contains "Mozilla" or "Opera"
        final String userAgent = request.getHeader("User-Agent");
        return userAgent != null
            && (userAgent.indexOf("Mozilla") > -1
                || userAgent.indexOf("Opera") > -1);


    boolean isAuthor = WCMMode.fromRequest(request) != WCMMode.DISABLED
            && !sling.getService(SlingSettingsService.class).getRunModes().contains("publish");

    // decide whether to redirect to the (wcm) login page, or to send a plain 404
    if (isAuthor
            && isAnonymousUser(request)
            && isBrowserRequest(request)) {
        Authenticator auth = sling.getService(Authenticator.class);
        if (auth != null) {
            try {
                auth.login(request, response);
                // login has been requested, nothing more to do
            } catch (NoAuthenticationHandlerException nahe) {
                bindings.getLog().warn("Cannot login: No Authentication Handler is willing to authenticate");
        } else {
            bindings.getLog().warn("Cannot login: Missing Authenticator service");


    // get here if authentication should not take place or if
    // no Authenticator service is available or if no
    // AuthenticationHandler is willing to authenticate
    // So we fall back to plain old 404/NOT FOUND    

<%-- Custom logic start for displaying 404 error message --%>
Integer scObject1 = (Integer) request.getAttribute("javax.servlet.error.status_code");
    int statusCode1 = (scObject1 != null)
            ? scObject1.intValue()
            : HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
if(isAuthor && slingRequest != null && statusCode1 == 404) { %>
<%@include file="/apps/sling/servlet/errorhandler/errormessage.jsp" %>
<% return; }%>
<%-- Custom logic ends for displaying 404 error message --%>

<%@include file="/libs/sling/servlet/errorhandler/default.jsp"%>
  • Create a new file by name of errormessage.jsp under /apps/sling/servlet/errorhandler. You can have it at any location and with any custom name.
  • Add below line of code to it and save.
<!DOCTYPE html>
<html lang="en">

    <title>Error | Adobe Experience Manager</title>
        <h1>ATTENTION <%= statusCode1 %></h1>

            Please consult your application support team.


That’s it , now we are done. Lets test the changes.

Use Case 1:- When customer user profile is not properly set up or he doesn’t have access to specific asset on author instance.

custom 404 asset ootb page

Use Case 2:- Accessing a page that does not exist. For Example: http://localhost:4502/content/we-retail/us/en/boy.html

custom 404 error page

Few important points to note:-

  • On an author instance, CQ WCM Debug Filter is enabled by default. This always results in the response code 200. The default error handler responds by writing the full stack trace to the response. In order for custom error handler to work on author , we need to disable CQ WCM Debug Filter.
disable cq wcm debug filter
  • On a publish instance, CQ WCM Debug Filter is always disabled (even if configured as enabled).
  • In AEM as Cloud Service, the CDN serves a generic error page when a 5XX error is received from the backend. In order to allow the actual response of the backend to pass through you need to add the following header to the response: x-aem-error-pass: true.
  • This works only for responses coming from AEM or the Apache/Dispatcher layer. Other unexpected errors coming from intermediate infrastructure layers will still display the generic error page.
  • As we are customizing the jsp file, its hard to troubleshoot for errors, always keep an eye on error logs they are listed there and can save your efforts.
Spread the love

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.