Vaadin 14+ Error/Exception Handling
There are two main entrypoints of error handling in Vaadin:
- The router exception handling, triggered during the navigation phase when the view is constructed, and
- Session’s ErrorHandler, triggered after the view has been rendered.
Router Exception Handling
Described in the Router Exception Handling Vaadin Tutorial. Triggered when the server failed to produce a view because of an exception. In the browser there is HTTP GET request which receives an HTTP 500 Internal Server Error page (actually oddly enough the HTTP GET returns 200 OK! Filed as #18781).
This handling is triggered when the Route class fails to initialize: for example an exception is thrown in the View class constructor, or Spring/CDI/JavaEE failed to inject beans into the view.
By default, the InternalServerError
Java class is rendered, which shows the exception stacktrace and returns HTTP 500 (actually 200 OK, see #18781).
The stacktrace is however shown only in development mode - it’s not shown in production mode.
See the InternalServerError
java class sources for more details. The exception stacktrace is always logged in to slf4j though.
To register a custom page, simply create a Java class, extending Vaadin Component,
implementing HasErrorParameter<Exception>
- Vaadin will then start using this class instead
of the default InternalServerError
one.
Example of the page in dev mode:
Example of the page in production mode:
ErrorHandler
Official Vaadin documentation. The ErrorHandler takes effect after the navigation is complete and the view is fully displayed. It handles exceptions coming from:
- Button click listeners, or generally any events coming from components.
- Asynchronous blocks run via
UI.access()
- however beware that theUI.getCurrent()
will be null in theErrorHandler
because of Vaadin bug #10533.
Note: It’s not possible to use one unified solution, for example only have an
ErrorHandler
handling the routing exceptions. The error handler is for example supposed to show a notification or a dialog detailing the steps to take (e.g. where to report the error). However, when the view failed to initialize and render, it might not even be possible to show a Dialog on a blank page since the JavaScript stuff might be missing etc.
By default the ErrorHandler only logs the exception to slf4j: it doesn’t display anything in the UI, so the user has no feedback that there was an exception. The best way is to display a dialog which says “We’re sorry but an application error occurred: #123871”. You would generate some random number, log it along with the exception and show it in the error message. That way, you can search for the stacktrace easily, without revealing the exception message to the user, since the message may contain sensitive data such as username or even password.
Another example of an ErrorHandler
implementation can be found at Vaadin Forums: The ‘Vaadin Error Handling’ Question.
You don’t have to extend Vaadin Servlet to customize ErrorHandler
- you can
add a session initializer which sets the ErrorHandler
as described in the
Vaadin SessionInitListener.
Then:
public class ApplicationServiceInitListener
implements VaadinServiceInitListener, SessionInitListener {
@Override
public void serviceInit(ServiceInitEvent event) {
event.getSource().addSessionInitListener(this);
}
@Override
public void sessionInit(SessionInitEvent event) {
event.getSession().setErrorHandler((ErrorHandler) event1 -> {
// TODO implement
});
}
}
For quick-and-dirty experiments you can set the error handler from your route:
@Route("")
public class MainView extends VerticalLayout {
public MainView() {
VaadinSession.getCurrent().setErrorHandler((ErrorHandler) event -> {
// TODO implement
});
}
}
Important: the UI.getCurrent()
might be null
in ErrorHandler, and in such case
it is not possible to display a Dialog or a Notification. In such case only log the exception,
don’t try to show a Dialog or a Notification.
#10533 made sure
that UI.getCurrent()
is non-null in more cases; please make sure you’re using newest Vaadin.
In certain very specific cases UI.getCurrent()
may still be null though; employ the null check in ErrorHandler.
Also if you encounter such case, please report it to Vaadin bug tracker.
When ErrorHandler itself throws
What happens when the ErrorHandler
itself throws? E.g.
@Route("")
public class MainView extends VerticalLayout {
public MainView() {
VaadinSession.getCurrent().setErrorHandler((ErrorHandler) event -> {
throw new RuntimeException("Simulated");
});
add(new Button("Click me", e -> {
throw new RuntimeException("Failed!");
}));
}
}
This is where things get interesting. Vaadin 24.3.5 doesn’t re-handle the exception with
the DefaultErrorHandler
- instead it lets the exception bubble out from VaadinServlet,
which causes the servlet container to catch the exception, log it to stdout and then respond with HTTP 500 Internal Server Error.
The browser will not display anything (unless the request is an UIDL request, in such case the “Internal Error” div is shown, see below).
This applies both for development and for production mode.
The best way is to wrap ErrorHandler in try{}catch
block; when it throws, log the exception
and then try to display the “Internal error #31313” dialog; when that fails, try to show a notification at least.
Internal Error
“Internal Error: Please notify the administrator. Take note of any unsaved data, and click here or press ESC to continue”.
This kind of error happens when the exception is thrown while the UIDL is being written, for example
from a Renderer
rendering contents of a Grid Column. Please see #14091
for more details. The “Internal Error” window is created in VaadinService.handleExceptionDuringRequest()
when it’s
probably too late to do any UIDL/component state repairs, and so the only way is to show the error window and then perform
full client state resynchronization.
With newer Vaadins (for example 23.3.33 or 24.3.5), when a custom ErrorHandler
is set, then the error handler receives the exception and the “Internal Error” window is not shown.
The reason is that the exception is handled way sooner, in StateTree.runExecutionsBeforeClientResponse()
, and at
that point it’s possible to recover from the exception without damaging the component state synchronization.
However, when the ErrorHandler
itself throws an exception, that exception reaches VaadinService.handleExceptionDuringRequest()
and the “Internal Error” is shown.
To reconfigure the error title and/or message, see Vaadin’s SystemMessages
class.
JavaScript errors
Third category of errors are JavaScript errors, for example when an incorrect JavaScript code is executed:
@Route("")
public class MainView extends VerticalLayout {
public MainView() {
add(new Button("Click me", e -> UI.getCurrent().getPage().executeJs("aksdalkdalk")));
}
}
Such exceptions are not logged in server-side slf4j unfortunately; see+vote for #17485.
Workaround is to call then()
as follows:
@Route("")
public class MainView extends VerticalLayout {
public MainView() {
add(new Button("Click me", e -> UI.getCurrent().getPage().executeJs("aksdalkdalk").then(
json -> {},
error -> { throw new RuntimeException("JS failed: " + error); }
)));
}
}
Now the exception goes through ErrorHandler
which by default logs it to slf4j, including the class name and line number,
so you can quickly fix the JavaScript script:
2024-02-22 10:40:21.910 [qtp1484171695-28] ERROR com.vaadin.flow.server.DefaultErrorHandler -
java.lang.RuntimeException: JS failed: ReferenceError: aksdalkdalk is not defined
at com.vaadin.starter.skeleton.MainView.lambda$new$2f364bb9$2(MainView.java:20)
at com.vaadin.flow.component.internal.PendingJavaScriptInvocation.completeExceptionally(PendingJavaScriptInvocation.java:112)
In development mode, the exception is logged in the JavaScript console in the browser and a red Vaadin logo starts blinking, to notify
you that there’s a problem in JavaScript. Unfortunately when you call then()
in Java, this functionality
gets disabled: #18782.
In production mode, the exception is logged in the JavaScript console in the browser
but the red Vaadin logo isn’t shown. Unfortunately when you call then()
in Java, this functionality
gets disabled: #18782.
See Vaadin - Troubleshooting the Browser for more tips on troubleshooting JavaScript, e.g. to deobfuscate the JavaScript stacktrace.
Bootstrap Error
When Vaadin Servlet fails to start, for example because Vite failed to compile your JavaScript files, the following screen is shown (this screen is servlet container dependent; on Tomcat it looks like this but on Jetty it might look differently):
To reproduce, just place this file into your project into frontend/src/my-test-element.js
:
class MyTestElement extends LitElement {
render() {
return html`
<h2>Hello ${throw}</h2>
`;
}
}
Customizable maybe by placing an error.html
into your webapp root folder.
Which one to override for customized error handling? Best Practices
Both. Usually the ErrorHandler
shows a Dialog or a Notification while the error
page shows a page, but the contents of the dialog and the page can be made similar.
In all cases:
- It’s always a good idea to log the exception; by default Vaadin always logs the exception.
- It’s always a good idea to notify the user about the error; Vaadin’s DefaultErrorHandler breaks this
so you should implement your own ErrorHandler, but make sure that your ErrorHandler doesn’t throw
an exception while processing the original exception.
- Remember that
UI.getCurrent()
might be null in ErrorHandler.
- Remember that
- JS
executeJs()
: Until #17485 is fixed, make sure to callthen()
; simply throw an exception in the error closure which will cause Vaadin to callErrorHandler
.