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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Understanding Spring Boot’s Default Error Handling and Response Formats

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentSpring BootHTTPError Handling
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.