flow
flow copied to clipboard
Element.executeJs() silently swallows any JavaScript errors
Description of the bug
Calling getElement().executeJs("someFunnyFunctionThatDoesntExist()")
from server-side will silently fail - the JS ReferenceError is not logged in server-side nor client-side.
I can see that the information about the failure is sent to the server-side, since the XHR message contains the following:
Sending xhr message to server: {"csrfToken":"456710bb-4724-4d05-8686-97f674488d18","rpc":[{"type":"channel","node":6,"channel":23,"args":["ReferenceError: someFunnyFunctionThatDoesntExist is not defined"]}],"syncId":24,"clientId":24} [FlowClient-d5d5e377.js:1:8091](http://localhost:8080/VAADIN/build/FlowClient-d5d5e377.js)
However, nothing is logged on the server-side.
Expected behavior
Something should be logged as WARN/ERROR, either in client-side or in server-side. Even better, an exception could be thrown in the server-side.
Minimal reproducible example
@Route("")
public class MainView extends VerticalLayout {
public MainView() {
add(new Button("Press me", e -> {
getElement().executeJs("someFunnyFunctionThatDoesntExist()")
.then((SerializableConsumer<JsonValue>) jsonValue -> Notification.show("Never gets called"));
}));
}
}
Versions
- Vaadin / Flow version: 24.1.5
- Java version: 17
That's because you're (accidentally) explicitly suppressing the error.
If you don't have any .then
on the execution, then there will be an error message in the browser console.
If you have a .then
, then the exception will be passed to the server and delivered to the second optional parameter of .then
. If no such parameter is passed, then the error is silently ignored and that's probably something that we should look into addressing.
getElement().executeJs("someFunnyFunctionThatDoesntExist()").then(
ignore -> Notification.show("Never gets called"),
ignore -> Notification.show("An error occurred"));
So your example code is really akin to this native Java example that also silently ignores the exception:
CompletableFuture.supplyAsync(() -> {
throw new RuntimeException();
}).thenAccept(value -> System.out.println("This never gets called"));
IMO we can try add a default handler when the seconds handler (custom error handler) is not given to executeJs
.
I don't know if it would be possible to switch between the default one and custom one.
If not, we can at lest document this behaviour in javadoc and on online docs.
Currently, if an error handler is not provided, the behavior is to log the error message at debug level (PendingJavaScriptInvocation
category).
What we do not log when the error handler is not given is the cancellation case.
I'd definitely suggest to use a default handler that logs an exception stacktrace into slf4j. That will help the programmer tremendously: he can quickly find where the offending javascript is executed, and can quickly fix the code in Java.
@mvysny PendingJavaScriptInvocation
already logs errors at debug level when there is no custom error handler.
Are you talking about a different handler? O the suggestion is to change the level from DEBUG to WARN or ERROR?
Yup, exactly. By default my log level for Vaadin is set to INFO or WARN (so that my log is not cluttered), causing the errors to be not logged. Logging them as WARN or ERROR would fix things for me.