ClassCastException: class elemental.json.impl.JreJsonString

Hi,

Posting this here first as I’m not sure it’s a Vaadin bug or application bug. As we upgraded from 24.7.8 to 24.8 we are seeing below stacktrace. It’s easily reproducible by just clicking any (anchor) link in the application homepage. So, suspecting something fundamental is going on. The homepage itself loads fine, which is perhaps odd. Rolling back to 24.7.8 makes the issue go away. It’s a rather large application and we cannot reproduce it on a smaller test application here. I have seen some work being done on removing the elemental json lib, could it be related? The stacktrace has appeared before in the Vaadin github: missing notification when the setMaxFileSize value exceeded? · Issue #107 · vaadin/vaadin-upload-flow · GitHub but this seems unrelated.

System exception:java.lang.ClassCastException: class elemental.json.impl.JreJsonString cannot be cast to class elemental.json.JsonObject (elemental.json.impl.JreJsonString and elemental.json.JsonObject are in unnamed module of loader org.springframework.boot.loader.launch.LaunchedClassLoader @47547132)
	at elemental.json.Json.parse(Json.java:53)
	at com.vaadin.flow.component.UI.browserNavigate(UI.java:1891)
	at com.vaadin.flow.component.ComponentEventBus.fireEventForListener(ComponentEventBus.java:244)
	at com.vaadin.flow.component.ComponentEventBus.handleDomEvent(ComponentEventBus.java:501)
	at com.vaadin.flow.component.ComponentEventBus.lambda$addDomTrigger$dd1b7957$1(ComponentEventBus.java:303)
	at com.vaadin.flow.internal.nodefeature.ElementListenerMap.lambda$fireEvent$2(ElementListenerMap.java:475)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at com.vaadin.flow.internal.nodefeature.ElementListenerMap.fireEvent(ElementListenerMap.java:475)
	at com.vaadin.flow.server.communication.rpc.EventRpcHandler.handleNode(EventRpcHandler.java:62)
	at com.vaadin.flow.server.communication.rpc.AbstractRpcInvocationHandler.handle(AbstractRpcInvocationHandler.java:79)
	at com.vaadin.flow.server.communication.ServerRpcHandler.handleInvocationData(ServerRpcHandler.java:568)
	at com.vaadin.flow.server.communication.ServerRpcHandler.lambda$handleInvocations$6(ServerRpcHandler.java:549)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at com.vaadin.flow.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:549)
	at com.vaadin.flow.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:376)
	at com.vaadin.flow.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:136)
	at com.vaadin.flow.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:63)
	at com.vaadin.flow.server.VaadinService.handleRequest(VaadinService.java:1852)
	at com.vaadin.flow.server.VaadinServlet.service(VaadinServlet.java:398)
	at com.vaadin.flow.spring.SpringServlet.service(SpringServlet.java:106)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)

Is perhaps your application using vaadin-router instead of the default react-router?

It might be useful if you could share the payload of the request that triggers the exception.

Unsure about the router question, but we define vaadin dependency like this:

<dependency>
      <groupId>com.vaadin</groupId>
      <artifactId>vaadin</artifactId>
      <exclusions>
        <exclusion>
          <groupId>com.vaadin</groupId>
          <artifactId>vaadin-dev</artifactId>
        </exclusion>
        <!-- excluded as per https://vaadin.com/blog/streamlined-vaadin-flow-setup-for-v24.4 -->
        <exclusion>
          <groupId>com.vaadin</groupId>
          <artifactId>copilot</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.vaadin</groupId>
          <artifactId>hilla</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.vaadin</groupId>
          <artifactId>hilla-dev</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.vaadin</groupId>
          <artifactId>flow-react</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

The request looks like this:

{"csrfToken":"0036a46d-c5aa-4c09-a08b-8e6b88a4fd36","rpc":[{"type":"event","node":1,"event":"ui-navigate","data":{"route":"admin/view-reporting-structure","query":"","appShellTitle":"","historyState":"","trigger":"link"}}],"syncId":6,"clientId":6}

Do you have reactEnable=false in vaadin-maven-plugin configuration and/or in application.properties?

If you set DEBUG level for com.vaadin.flow.server.frontend.FrontendUtils category, at some point you should see something like Auto-detected client-side router to use: ....

The maven plugin is configured like this:

<plugin>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-maven-plugin</artifactId>
            <version>${vaadin.version}</version>
            <configuration>
              <!--
              don't set this to true as it exposes us to possible npm related build issues that we don't care about
               -->
              <ciBuild>false</ciBuild>
              <!-- this forces generation of prod.bundle when doing a normal maven build -->
              <forceProductionBuild>true</forceProductionBuild>
              <!-- to preserve fast startup times -->
              **<reactEnable>false</reactEnable>**
            </configuration>
            <executions>
              <execution>
                <goals>
                  <goal>prepare-frontend</goal>
                  <goal>build-frontend</goal>
                </goals>
                <phase>compile</phase>
              </execution>
            </executions>
          </plugin>

I cannot reproduce the issue using anchor or router links with 24.8.2.
The main difference I can spot is that in my case the vaadin-navigate event payload has always "historyState":null whereas you have "historyState":"".

This is probably the cause of the exception, but I’m unable to reproduce.
Can you provide an example based on GitHub - vaadin/skeleton-starter-flow-spring: Default project template for Vaadin using Spring Boot?

Ok, I could reproduce, but only manually changing the browser history state in the browser console or adding a call like UI.getCurrent().getPage().getHistory().replaceState(Json.create(""), ""); on the server side.

Do you have similar code on your project?

We have indeed a UI initlistener that does below logic, because the application is loaded “portal-style” into an iframe.

      event
          .getSource()
          .addUIInitListener(
              uiInitEvent -> {
                final UI ui = uiInitEvent.getUI();

                try {
                  ui.addAfterNavigationListener(
                      e -> {

                        // using js to fetch the full URL
                        ui.getPage()
                            .executeJs("return window.top.location.pathname;")
                            .then(
                                String.class,
                                fullUrl -> {
                                  // skip execution if the URL contains 'portal'
                                  if (fullUrl != null && fullUrl.contains("portal")) {
                                    return;
                                  }

                                  // needed to set the correct browser url when using via the
                                  // portal
                                  // as we're loaded into an iframe there
                                  ui.getPage()
                                      .executeJs(
                                          "window.top.history.replaceState('', '', $0)",
                                          BROWSER_URL_PREFIX
                                              + e.getLocationChangeEvent()
                                                  .getLocation()
                                                  .getPathWithQueryParameters());
                                });
                      });
                } catch (Exception e) {
                  log.error("Error executing js to update url ", e);
                }
              });

Interesting. I think that if you change the above to window.top.history.replaceState(null, null, $0), the exception should go away

@marcoc_753 thank you, this indeed fixes the issue. I’ll put this down to unorthodox browser manipulation from our side. It’s for this reason that I normally reject ui.getPage().executeJs() code in our applications, we just don’t know enough about it.