> Markdown version of [Router Exception Handling](https://vaadin.com/docs/next/flow/routing/exceptions). Section index: [llms.txt](https://vaadin.com/docs/next/flow/llms.txt)

# Router Exception Handling

`Router` provides special support for navigation target exceptions. When an unhandled exception is thrown during navigation, an error view is shown.

Exception targets work the same way as regular navigation targets, but they don’t typically have a specific `@Route` because they’re shown for arbitrary URLs.

## <a id="error-resolving"></a>Error Resolving

Errors in navigation are resolved to a target that’s based on the exception type thrown during navigation.

At startup, all classes implementing the `HasErrorParameter<T extends Exception>` interface are collected for use as exception targets during navigation. An example of such a class is `RouteNotFoundError`. It’s included by default in the framework and is used to resolve errors related to the router’s `NotFoundException`.

In the example here, `RouteNotFoundError` defines the default target for the `NotFoundException` that’s shown when there is no target for the given URL:

```java
@Tag(Tag.DIV)
public class RouteNotFoundError extends Component
       implements HasErrorParameter<NotFoundException> {

    @Override
    public int setErrorParameter(BeforeEnterEvent event,
          ErrorParameter<NotFoundException> parameter) {
        getElement().setText("Could not navigate to '"
                    + event.getLocation().getPath()
                    + "'");
        return HttpServletResponse.SC_NOT_FOUND;
    }
}
```

This returns a `404` HTTP response, and displays the text specified in the parameter to `setText()`. Exceptions are matched first by an exception cause, then by an exception super type.

The `404` `RouteNotFoundError` for `NotFoundException`, and `500` `InternalServerError` for `java.lang.Exception` are implemented by default.

Also by default, the initial request loads first the client-side bootstrapping with an HTTP 200 response. An error page is rendered later in another request, which returns an initial JSON data fragment, including the error page. The HTTP 404 response from `RouteNotFoundError` is only processed on the server side. It isn’t returned to the browser as an HTTP code of the response.

If you enable the [`eagerServerLoad` property](https://vaadin.com/docs/next/flow/configuration/properties.md#properties), the initial JSON data fragment is part of the first request’s response. It includes the `RouteNotFoundError` page with an HTTP 404 response.

## <a id="custom-exception-handlers"></a>Custom Exception Handlers

You can replace a default exception handler with any `HasErrorParameter` implementation that handles the same exception type. Your handler can extend the default one, or implement the interface directly. The example here extends `RouteNotFoundError` to show a custom "route not found" handler that uses a custom application layout:

```java
@ParentLayout(MainLayout.class)
@AnonymousAllowed // (1)
public class CustomNotFoundTarget
        extends RouteNotFoundError {

    @Override
    public int setErrorParameter(BeforeEnterEvent event,
          ErrorParameter<NotFoundException> parameter) {
        getElement().setText(
                "My custom not found class!");
        return HttpServletResponse.SC_NOT_FOUND;
    }
}
```

1. Lets navigation access control render the error view for unauthenticated users. Use `@PermitAll` instead if only authenticated users should see it. Under Spring Security, this annotation alone isn’t enough — see the note below.

> **Important: Spring Security Integration**
>
> When using `VaadinSecurityConfigurer` (or the deprecated `VaadinWebSecurity`), making a custom not-found view work requires two separate things:
>
> 1. **Annotate the view** so that navigation access control allows it to render — for example with `@AnonymousAllowed` (as above) or `@PermitAll`.
>
> 2. **Relax the catch-all HTTP rule** so that requests for non-existing pages reach the Vaadin servlet. By default, any request that doesn’t match a Vaadin route — including a request for a non-existing page — is denied at the HTTP layer before navigation occurs, so the custom view is never rendered. The annotation by itself doesn’t change this, because the request is blocked before the framework resolves the not-found target.
>
> See [Vaadin Security Configurer](https://vaadin.com/docs/next/flow/security/vaadin-security-configurer.md#configurer) for how to relax the catch-all rule with `anyRequest()`, and [Protect Views](https://vaadin.com/docs/next/building-apps/security/protect-views.md) for more on view-level annotations.

Exception targets may define a parent layout with `@ParentLayout`. `BeforeEnterEvent` and `AfterNavigationEvent` are still sent, as with normal navigation.

Each exception type can have only one handler. A handler of your own always replaces a default one, even if it doesn’t extend it. If two of your own handlers handle the same exception type, one of them has to extend the other, and the subclass is used. Otherwise, the application fails at startup with an `InvalidRouteConfigurationException`.

The following example assumes an application `Dashboard` that collects and shows widgets to users. Only authenticated users are allowed to see protected widgets. If the collection instantiates a `ProtectedWidget` in error, the widget itself checks authentication on creation and throws an `CustomAccessDeniedException.`

The unhandled exception propagates during navigation and is handled by the `AccessDeniedExceptionHandler` that keeps the `MainLayout` with its menu bar, but displays information that an exception has occurred.

```java
@Route(value = "dashboard", layout = MainLayout.class)
@Tag(Tag.DIV)
public class Dashboard extends Component {
    public Dashboard() {
        init();
    }

    private void init() {
        getWidgets().forEach(this::addWidget);
    }

    public void addWidget(Widget widget) {
        // Implementation omitted
    }

    private Stream<Widget> getWidgets() {
        // Implementation omitted, gets faulty state
        // widget
        return Stream.of(new ProtectedWidget());
    }
}

public class ProtectedWidget extends Widget {
    public ProtectedWidget() {
        if (!AccessHandler.getInstance()
                .isAuthenticated()) {
            throw new CustomAccessDeniedException(
                    "Unauthorized widget access");
        }
        // Implementation omitted
    }
}

@Tag(Tag.DIV)
public abstract class Widget extends Component {
    public boolean isProtected() {
        // Implementation omitted
        return true;
    }
}

@Tag(Tag.DIV)
@ParentLayout(MainLayout.class)
@PermitAll
public class AccessDeniedExceptionHandler
     extends Component
     implements HasErrorParameter<CustomAccessDeniedException>
{

    @Override
    public int setErrorParameter(BeforeEnterEvent event,
            ErrorParameter<CustomAccessDeniedException>
                    parameter) {
        getElement().setText(
            "Tried to navigate to a view without "
            + "correct access rights");
        return HttpServletResponse.SC_FORBIDDEN;
    }
}

public class CustomAccessDeniedException extends RuntimeException {
}
```

The example above uses `@PermitAll`, but `@RolesAllowed` can also be used. `@AnonymousAllowed` isn’t recommended for error views handling access denied exceptions, as it exposes information about access restrictions to the anonymous users.

## <a id="rerouting-to-error-view"></a>Rerouting to Error View

It’s possible to reroute from the `BeforeEnterEvent` and `BeforeLeaveEvent` to an error view registered for an exception. You can use one of the `rerouteToError()` method overloads. You need only to add the exception class to the target, and a custom error message, where necessary.

This example shows how to reroute to an error view:

```java
public class AuthenticationHandler
        implements BeforeEnterObserver {
    @Override
    public void beforeEnter(BeforeEnterEvent event) {
        Class<?> target = event.getNavigationTarget();
        if (!currentUserMayEnter(target)) {
            event.rerouteToError(
                    CustomAccessDeniedException.class);
        }
    }

    private boolean currentUserMayEnter(
            Class<?> target) {
        // implementation omitted
        return false;
    }
}
```

If the rerouting method catches an exception, you can use the `rerouteToError(Exception, String)` method to set a custom message. Re-routing to a custom access denial view is also possible with annotation `@AccessDeniedErrorRouter`. See [Customizing Error Messages for Unauthorized Views](https://vaadin.com/docs/next/flow/advanced/custom-error-handler.md) for more on this.

This next example shows a blog sample error view with a custom message:

```java
@Tag(Tag.DIV)
public class BlogPost extends Component
        implements HasUrlParameter<Long> {

    @Override
    public void setParameter(BeforeEvent event,
            Long parameter) {
        removeAll();

        Optional<BlogRecord> record =
                getRecord(parameter);

        if (!record.isPresent()) {
            event.rerouteToError(
                   IllegalArgumentException.class,
                   getTranslation("blog.post.not.found",
                        event.getLocation().getPath()));
        } else {
            displayRecord(record.get());
        }
    }

    private void removeAll() {
        // NO-OP
    }

    private void displayRecord(BlogRecord record) {
        // NO-OP
    }

    public Optional<BlogRecord> getRecord(Long id) {
        // Implementation omitted
        return Optional.empty();
    }
}

@Tag(Tag.DIV)
@AnonymousAllowed
public class FaultyBlogPostHandler extends Component
  implements HasErrorParameter<IllegalArgumentException>
{

    @Override
    public int setErrorParameter(BeforeEnterEvent event,
            ErrorParameter<IllegalArgumentException>
                    parameter) {
        Span message = new Span(
                parameter.getCustomMessage());
        getElement().appendChild(message.getElement());

        return HttpServletResponse.SC_NOT_FOUND;
    }
}
```

`HasErrorParameter` error view needs an access control annotation so that Vaadin Flow allows navigation to it. The example above uses `@AnonymousAllowed`, but `@PermitAll` or `@RolesAllowed` can also be used.

## <a id="error-view-for-exception-during-rpc-call"></a>Error View for Exception during RPC Call

Vaadin Flow shows the registered error views if an exception occurs outside routing and rerouting. An example of this is when a button-click event is fired during a remote procedure call (i.e., RPC) on a server.

This enables updating the current view content to a registered `HasErrorParameter<T extends Exception>` that handles the exact exception thrown for RPC events.

If you’re using a custom `ErrorHandler`, see the [Showing Error Parameter Views For Non-Navigation Exceptions](https://vaadin.com/docs/next/flow/advanced/custom-error-handler.md) page for information on this feature.

If this redirection isn’t desired, you can provide a custom error handler implementation without a redirect to error view. See the [Custom Error Handling](https://vaadin.com/docs/next/flow/advanced/custom-error-handler.md) page for more on this.

`F4039D66-C9C5-4CEE-B49A-F1224B46C5E8`
