Error Handling

This article covers error handling on the client side of a Fusion application. A robust client implementation should be able to deal with the most common cases. This includes invalid endpoint calls, errors on the server side, and network outages.

Endpoint Errors

Fusion determines the success of an endpoint call inspecting the HTTP status code. The server returns 200 OK code when it is able to successfully process the request: deserialize the method body, find and execute the particular method in the endpoint, and serialize its return value into a response. If the status code of the response is not 200 OK, Fusion throws an error on the client side. The available parameters in the error and the specific class of the thrown error depend on the failure mode. The most common ones are described below.

Missing Endpoint

If the request addresses an endpoint or a method name not present on the backend, the server responds with 404 Not Found and Fusion raises an error of type EndpointError.

Parameter Validation Error

If the method called in the request exists on the backend, but the parameter count and types do not match the endpoint method (see Type conversion between JavaScript and Java for more details about the type conversion rules), the server responds with 400 Bad Request and Fusion raises an error of type EndpointValidationException. The error instance contains a field validationErrorData holding validation error information for each invalid parameter.

For example, given the following endpoint expecting a java.time.LocalDate parameter:

package com.vaadin.demo.fusion.errorhandling;

import java.time.LocalDate;

import com.vaadin.flow.server.connect.Endpoint;
import com.vaadin.flow.server.auth.AnonymousAllowed;

@Endpoint
public class DateEndpoint {

    @AnonymousAllowed
    public LocalDate getTomorrow(LocalDate date) {
        return date.plusDays(1);
    }
}

A call with an illegal data parameter raises an EndpointValidationException with information about which parameters failed validation:

import { EndpointValidationError } from '@vaadin/flow-frontend';

import { DateEndpoint } from 'Frontend/generated/DateEndpoint';

export async function callEndpoint() {
  try {
    // pass an illegal date
    const tomorrow = await DateEndpoint.getTomorrow('2021-02-29');
    console.log(tomorrow);
    // handle result...
  } catch (error) {
    if (error instanceof EndpointValidationError) {
      error.validationErrorData.forEach(({ parameterName, message }) => {
        console.warn(parameterName); // "date"
        console.warn(message); // "Unable to deserialize an endpoint method parameter into type 'java.time.LocalDate'"
      });
    } else {
      // handle other error types...
    }
  }
}

Note that when using server-side form validation, validation exceptions from the server are handled automatically by the form binder.

Server Side Errors

If the endpoint exists and its parameters could be passed, but its execution raises a Java runtime exception, the server responds with 500 Internal Server Error and Fusion raises an error of type EndpointError. As a special case, if the server-side exception is an instance of com.vaadin.flow.server.connect.exception.EndpointException or its subclass, the server instead responds with 400 Bad Request and the exception type and message passed to the EndpointException in Java are available in the EndpointError instance via the type and message attributes. For example, given the following endpoint implementation:

package com.vaadin.demo.pwa.offline;

import com.vaadin.flow.server.connect.Endpoint;
import com.vaadin.flow.server.connect.exception.EndpointException;

@Endpoint
public class DataEndpoint {

    public String getViewData() {
        throw new EndpointException("Not implemented");
    }
}

The following client-side call to the endpoint method logs the error message and exception type:

import { EndpointError } from '@vaadin/flow-frontend';

import { DataEndpoint } from 'Frontend/generated/DataEndpoint';

export async function callEndpoint() {
  try {
    await DataEndpoint.getViewData();
  } catch (error) {
    console.warn(error.message); // "Not implemented"
    if (error instanceof EndpointError) {
      console.warn(error.type); // "com.vaadin.flow.server.connect.exception.EndpointException"
    }
  }
}

Network Errors

When the server is not reachable due to outage or network disruption, an endpoint call results in a low-level network error, different from EndpointError. Applications that support offline mode can wrap endpoint calls with exception-handling code returning a fallback value, by distinguishing between the error classes as follows:

import { EndpointError } from '@vaadin/flow-frontend';

// import the remote endpoint
import { DataEndpoint } from 'Frontend/generated/DataEndpoint';

// wrap endpoint calls to return fallback data when offline
export async function getViewData() {
  try {
    return await DataEndpoint.getViewData();
  } catch (e) {
    if (!(e instanceof EndpointError)) {
      // network failure: return fallback data
      return [];
    } else {
      // endpoint reached but returned abnormal status code:
      // pass exception on to caller
      throw e;
    }
  }
}

Also see this article on caching endpoint data in local storage using a generic wrapper.

Unexpected Response Contents

If the server replies with another response than 200 OK, and the string contained in the response is not valid JSON, an EndpointResponseError is raised. The exception contains the response text as message and the Response object in the response field.