JSP provides two page directives for error handling:
When an exception is thrown on a JSP with errorPage set, the container automatically forwards the request to the designated error page, passing the exception as an attribute.
<%-- divide.jsp: page that may throw an exception --%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
errorPage="error.jsp" %>
<!DOCTYPE html>
<html>
<head><title>Division</title></head>
<body>
<h2>Division Result</h2>
<%
int a = Integer.parseInt(request.getParameter("a"));
int b = Integer.parseInt(request.getParameter("b"));
// If b == 0, throws ArithmeticException
// container forwards to error.jsp automatically
int result = a / b;
out.println("Result: " + result);
%>
</body>
</html>
<%-- error.jsp: designated error page --%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
isErrorPage="true" %>
<!DOCTYPE html>
<html>
<head><title>Error</title></head>
<body>
<h2 style="color:red;">An Error Occurred</h2>
<%-- 'exception' implicit object is available because isErrorPage="true" --%>
<p><strong>Error Type:</strong> <%= exception.getClass().getName() %></p>
<p><strong>Message:</strong> <%= exception.getMessage() %></p>
<p><a href="javascript:history.back()">Go Back</a></p>
</body>
</html>
The web.xml deployment descriptor allows you to define global error pages for the entire web application using <error-page> elements. You can map by HTTP error code or by exception type.
<web-app>
<!-- Map HTTP 404 to a custom JSP page -->
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/views/error-404.jsp</location>
</error-page>
<!-- Map HTTP 500 to a custom JSP page -->
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/views/error-500.jsp</location>
</error-page>
<!-- Map a specific exception type -->
<error-page>
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/WEB-INF/views/arithmetic-error.jsp</location>
</error-page>
<!-- Catch-all for any unhandled exception -->
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/WEB-INF/views/generic-error.jsp</location>
</error-page>
</web-app>
<%-- WEB-INF/views/error-404.jsp --%>
<%@ page language="java" contentType="text/html; charset=UTF-8" isErrorPage="true" %>
<!DOCTYPE html>
<html>
<head><title>404 - Page Not Found</title></head>
<body style="text-align:center; padding:60px;">
<h1 style="font-size:80px; color:#e74c3c;">404</h1>
<h2>Page Not Found</h2>
<p>The page you are looking for does not exist.</p>
<%-- Access error attributes set by the container --%>
<%
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
String requestUri = (String) request.getAttribute("javax.servlet.error.request_uri");
if (requestUri != null) {
out.println("<p>Requested URL: <code>" + requestUri + "</code></p>");
}
%>
<a href="${pageContext.request.contextPath}/">Go to Home</a>
</body>
</html>
When a JSP is designated as an error page (isErrorPage="true"), the exception implicit object is available. The servlet container also sets several request attributes you can use:
| Attribute | Description |
|---|---|
| javax.servlet.error.status_code | HTTP status code (Integer) |
| javax.servlet.error.exception_type | Class of the exception (Class) |
| javax.servlet.error.message | Exception message (String) |
| javax.servlet.error.exception | The actual exception object (Throwable) |
| javax.servlet.error.request_uri | URI that caused the error (String) |
| javax.servlet.error.servlet_name | Name of the servlet that caused the error |
<%-- WEB-INF/views/error-500.jsp --%>
<%@ page language="java" contentType="text/html; charset=UTF-8" isErrorPage="true" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head><title>500 - Internal Server Error</title></head>
<body style="padding:40px;">
<h1 style="color:#e74c3c;">500 - Internal Server Error</h1>
<p>Something went wrong on our end. Please try again later.</p>
<%-- Show details only in development mode --%>
<%
String env = application.getInitParameter("environment");
if ("development".equals(env) && exception != null) {
%>
<div style="background:#f8d7da;padding:15px;border-radius:5px;margin-top:20px;">
<h4>Debug Info (development only):</h4>
<p><strong>Exception:</strong> <%= exception.getClass().getName() %></p>
<p><strong>Message:</strong> <%= exception.getMessage() %></p>
<p><strong>URI:</strong>
<%= request.getAttribute("javax.servlet.error.request_uri") %></p>
<pre style="overflow:auto;"><%
java.io.StringWriter sw = new java.io.StringWriter();
exception.printStackTrace(new java.io.PrintWriter(sw));
out.println(sw.toString());
%></pre>
</div>
<% } %>
<p><a href="${pageContext.request.contextPath}/">Return to Home</a></p>
</body>
</html>
An error page can replace a response only while the response remains uncommitted. Avoid flushing output before operations that may fail, and do not attempt a forward after bytes have already been sent. For asynchronous processing, register the appropriate listener and complete or dispatch the request through one defined path.
Log the request correlation identifier, exception type, root cause, route, and safe operational context. Do not log passwords, session identifiers, raw personal data, or entire request bodies by default. Return a stable user-facing message and status code, then test both container error mappings and failures raised after partial work.
<%@ page contentType="text/html;charset=UTF-8" %>
<main>
<h1>JSP</h1>
<p>Use JSP for the view layer and keep business logic in a servlet or service class.</p>
</main>
request.setAttribute("lessonTitle", "JSP");
request.setAttribute("reviewed", Boolean.TRUE);
request.getRequestDispatcher("/WEB-INF/views/lesson.jsp").forward(request, response);
// The servlet prepares data; the JSP renders it.
It makes the exception implicit object available on the designated JSP error page.
No. Log technical detail and show the user a safe message.
It centralizes handling by status code or exception type instead of adding logic to every JSP.
Explore 500+ free tutorials across 20+ languages and frameworks.