Exception Handling

About

In microservices or any distributed systems, remote API calls are inherently unreliable due to:

  • Network latency or timeouts

  • API version mismatches

  • Temporary service outages

  • Unauthorized or invalid requests

  • Resource not found (e.g., user doesn’t exist)

Instead of letting these issues break our application flow, proper exception handling helps us:

  • Provide informative responses to clients

  • Avoid cascading failures

  • Enable automated fallbacks

  • Log and trace failures for observability

  • Enforce clean separation of concerns between service call logic and failure behavior

How OpenFeign Handles Exceptions ?

OpenFeign does not throw HttpClientErrorException or HttpServerErrorException like RestTemplate. Instead:

  • It throws FeignException, a base class for all client errors.

  • Subtypes like FeignException.NotFound, FeignException.BadRequest represent specific HTTP statuses.

  • The exception encapsulates the raw response, status code, and response body which we can inspect or log.

1. Client-Side I/O Exceptions (Request-Time Errors)

These exceptions happen before the HTTP request even reaches the server. They usually indicate a problem in the request pipeline itself, such as:

  • Network unreachable (e.g., DNS resolution fails, host unreachable)

  • Timeout while establishing a connection

  • TLS handshake failure

  • Serialization error while preparing the request body

  • Connection reset by peer

These are different from typical HTTP response errors (like 404 or 500), because no HTTP response is returned.

How Does OpenFeign Represent These ?

When such exceptions occur:

  • OpenFeign throws FeignException or wraps lower-level exceptions (e.g., IOException, SocketTimeoutException) as RetryableException or raw RuntimeException.

In Spring Cloud OpenFeign, we can intercept these exceptions in:

  • A global @ControllerAdvice

  • A custom Feign ErrorDecoder if Feign tries to wrap it

  • A fallback method if configured

Example: Handling I/O Failures in a Safe Way

Let’s say a downstream service is unavailable, and the client fails at request time.

Feign Client Definition

Fallback Implementation

This will catch I/O-level failures and prevent the caller from crashing.

Catching Low-Level Exceptions (Manually)

If we want full control and don’t use a fallback, catch FeignException or nested IOException:

Better: Custom ErrorDecoder with Logging

Even request-time failures can be caught in a custom decoder:

Detecting Request-Time vs Response-Time Failures

To detect request-time I/O errors, we can configure Feign to use a Retryer or add a custom interceptor and logger.

2. Server Response Exceptions (Non-2xx Responses)

These occur when the request successfully reaches the server, but the server returns a non-2xx HTTP status code, such as:

  • 404 Not Found

  • 400 Bad Request

  • 401 Unauthorized

  • 500 Internal Server Error

These aren't client-side I/O errors they represent a valid HTTP response indicating server-side rejection or failure.

How OpenFeign Handles Them ?

OpenFeign by default throws a FeignException (a subclass of RuntimeException) when the server returns a non-successful HTTP status.

We can handle these exceptions by:

  • Catching FeignException in our business code

  • Defining a custom ErrorDecoder

  • Using fallbacks with @FeignClient(fallback = …)

Exception Type Mapping in Feign

HTTP Status
Feign Exception Class

4xx

FeignException.ClientError

5xx

FeignException.ServerError

Other

FeignException

Example: Basic Feign Client

Feign Client Interface

Calling the Client and Handling Server Errors

Custom ErrorDecoder to Handle Server Responses Gracefully

Instead of handling exceptions everywhere, define a central place to decode them.

Fallback Option: Safe Fallback for Errors

3. Deserialization and Response Mapping Errors

Deserialization errors occur after a successful HTTP response (i.e., a 2xx status), but when Feign tries to convert the JSON/XML body into a Java object (POJO) and fails.

These are runtime exceptions and common reasons include:

  • Mismatched or missing fields in the POJO

  • Incorrect data types (e.g., expecting int but receiving a string)

  • Invalid or unexpected response structure

  • Jackson misconfiguration

How Feign Handles Deserialization ?

Feign uses Jackson (via Spring Cloud OpenFeign) by default to deserialize the HTTP response into a Java object. If deserialization fails, it throws:

or

These typically bubble up as:

Example Scenario

Feign Client

Account POJO (Incorrect)

JSON Response

Here, deserialization will fail because Jackson looks for userName, but the JSON has username.

How to Fix / Prevent

1. Match Field Names Exactly

Use @JsonProperty if field names differ.

2. Enable Logging for Debugging

Catching Deserialization Errors Globally

Wrap our call in a try-catch and catch mapping exceptions:

Note: Jackson exceptions might get wrapped inside Feign or not be thrown directly, so sometimes using a fallback or decoding error body helps.

Using a Custom Decoder

Override Jackson decoder for finer control:

Last updated