Skip to content

Confusing error-handling logic in FunctionalScalarDeserializer (Wrapped with exceptions) #5608

@JooHyukKim

Description

@JooHyukKim

Follow up on Issue #4004 and PR #5595

Is your feature request related to a problem? Please describe.

Long story short, error types are confusing and sometimes misleading.

Jackson 3.1 introduced FunctionalScalarDeserializer that provides a rich and extensible deserialization model, but error handling in user-provided deserializers is currently too loose, leading to loss of semantic meaning
in exception types.

Problematic scenario

Consider the following test:

@Test
public void justThrowErrorEvenWithValidFormat() throws Exception
{
    SimpleModule module = new SimpleModule("test");
    module.addDeserializer(Bar.class,
            new FunctionalScalarDeserializer<>(Bar.class, (p, ctx) -> {
                // 1) Blindly throw user/business exception
                throw new IllegalArgumentException(
                        "BiFunction error: " + p.getValueAsString());
            }));

    ObjectMapper mapper = jsonMapperBuilder()
            .addModule(module)
            .build();

    try {
        // 2) JSON input is fully valid
        mapper.readValue("{\"bar\":\"fieldValue\"}", BarWrapper.class);
        fail();
    } catch (InvalidFormatException e) {
        // 3) Jackson wraps the exception as InvalidFormatException
        verifyException(e, "Cannot deserialize");
    }
}

In this case:

  1. User code throws an arbitrary exception (IllegalArgumentException)
  2. The JSON input is valid
  3. Jackson wraps the failure as InvalidFormatException <--- why?

From a user perspective, encountering InvalidFormatException strongly suggests that the input value itself is invalid or malformed.
However, in this scenario the failure is entirely due to application/domain logic,
not input format.

This makes it difficult for users to correctly interpret and classify errors
(e.g. client error vs server error).

There could be more cases.

Describe the solution you'd like

Goal: Make exception wrapping from user-provided deserializers predictable

Jackson should apply deterministic wrapping rules when exceptions are thrown from user-provided deserialization callbacks (for example,
FunctionalScalarDeserializer), so that the resulting exception type reflects
the nature of the failure.


Proposed behavior

  1. Provide guidelines on HOW user should throw error (and which type)
  2. DO improve Exception handling logic within FunctionalScalarDeserializer

Below is my rough idea of mentioned guideline.

/**
 * A general-purpose deserializer that uses a {@link Function} or {@link BiFunction}
 * to convert JSON scalar values (strings, numbers, booleans) into target type instances.
 * <p>
 * This deserializer is primarily designed for String-based conversions but also
 * supports other JSON scalar types via {@code getValueAsString()} coercion.
 * Non-scalar JSON values (arrays, objects, embedded objects) are rejected.
 * <p>
 * <b>Error handling guidelines:</b>
 * <ul>
 *   <li>Exceptions of type {@link com.fasterxml.jackson.databind.exc.InvalidFormatException}
 *       thrown by user code are treated as explicit signals of invalid input values
 *       and are propagated as-is.</li>
 *   <li>{@link com.fasterxml.jackson.databind.JsonMappingException} may be thrown by
 *       user code to indicate a deliberate mapping failure and will be propagated.</li>
 *   <li>Other runtime exceptions thrown by user code are considered application or
 *       domain failures and may be wrapped as {@link com.fasterxml.jackson.databind.JsonMappingException}
 *       rather than being reported as input-format errors.</li>
 * </ul>
 * <p>
 * Usage examples:
 * ...... rest omitted.....

Exception handling rules

Thrown by user code Jackson behavior
InvalidFormatException Propagate as-is
JsonMappingException Propagate as-is
Other Exceptions Wrap in JsonMappingException

Resulting Benefit

  • Valid JSON input is not reported as format error
  • User intent is preserved when signaling mapping failures
  • Custom deserializer error behavior becomes consistent and predictable

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.xIssues to be only tackled for Jackson 3.x, not 2.x

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions