Understanding Spring Boot’s Default Error Handling and Response Formats
This article explains how Spring Boot 3.0.5 processes errors, registers a default /error page, and returns either HTML or JSON responses based on the Accept header, detailing the underlying servlet and Tomcat mechanisms with code examples.
Environment
Spring Boot 3.0.5
Error Message Formats
Example controller that triggers an exception:
@RestController
@RequestMapping("/demo")
public class DemoController {
@GetMapping("/index")
public Object index() {
System.out.println(1 / 0);
return "/demo/index";
}
}When this endpoint is accessed, Spring Boot returns an HTML error page if the request Accept header is text/html, and a JSON error payload if the header is application/json.
Error Handling Principle
During startup Spring Boot refreshes the application context, creates a servlet web server, and registers error pages.
public abstract class AbstractApplicationContext {
public void refresh() {
onRefresh();
}
} public class ServletWebServerApplicationContext {
protected void onRefresh() {
super.onRefresh();
try {
// create web server
createWebServer();
} catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
// assume Tomcat, factory = TomcatServletWebServerFactory
this.webServer = factory.getWebServer(getSelfInitializer());
}
} public class TomcatServletWebServerFactory {
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
// ...
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
// ... configure context
configureContext(context, initializers);
}
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
// register error pages from Spring's ErrorPage definitions (similar to web.xml)
for (ErrorPage errorPage : getErrorPages()) {
org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
tomcatErrorPage.setLocation(errorPage.getPath());
tomcatErrorPage.setErrorCode(errorPage.getStatusCode());
tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
context.addErrorPage(tomcatErrorPage);
}
}
}The factory implements ErrorPageRegistry, allowing Spring to add error pages via addErrorPages:
public abstract class AbstractConfigurableWebServerFactory implements ConfigurableWebServerFactory {
public void addErrorPages(ErrorPage... errorPages) {
this.errorPages.addAll(Arrays.asList(errorPages));
}
}Spring registers an ErrorPageRegistrarBeanPostProcessor that discovers all ErrorPageRegistrar beans and invokes their registerErrorPages method.
public class ErrorPageRegistrarBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof ErrorPageRegistry) {
postProcessBeforeInitialization((ErrorPageRegistry) bean);
}
return bean;
}
private void postProcessBeforeInitialization(ErrorPageRegistry registry) {
for (ErrorPageRegistrar registrar : getRegistrars()) {
registrar.registerErrorPages(registry);
}
}
private Collection<ErrorPageRegistrar> getRegistrars() {
if (this.registrars == null) {
this.registrars = new ArrayList<>(this.beanFactory.getBeansOfType(ErrorPageRegistrar.class, false, false).values());
this.registrars.sort(AnnotationAwareOrderComparator.INSTANCE);
this.registrars = Collections.unmodifiableList(this.registrars);
}
return this.registrars;
}
}The auto‑configuration class ErrorMvcAutoConfiguration defines a default ErrorPageCustomizer that reads server.error.path (default /error) and registers it.
public class ErrorMvcAutoConfiguration {
@Bean
public ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {
return new ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);
}
static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {
private final ServerProperties properties;
private final DispatcherServletPath dispatcherServletPath;
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
errorPageRegistry.addErrorPages(errorPage);
}
}
}Thus, after the container starts, Spring Boot registers an error page at /error.
Default Error Controller
The auto‑configuration also provides a BasicErrorController that handles requests to the error path.
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
// render HTML error view
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}
}When an exception occurs, Spring Boot returns either an HTML error page or a JSON error body depending on the client’s Accept header, using the default /error endpoint and the BasicErrorController implementation.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
