FasterXML/jackson-databind · error · InvalidFormatException

`DeserializationProblemHandler.handleNullForPrimitives()` fo

Error message

`DeserializationProblemHandler.handleNullForPrimitives()` for type %s returned value of type %s

What it means

When a JSON null is encountered for a primitive Java field and a DeserializationProblemHandler is registered, Jackson gives the handler a chance to substitute a default value via handleNullForPrimitives(). If the handler returns an object whose type is not compatible (assignable) to the target primitive wrapper type, Jackson throws InvalidFormatException rather than silently mis-boxing the value. This is a sanity guard against buggy/problematic handlers that would otherwise produce ClassCastException downstream.

Source

Thrown at src/main/java/tools/jackson/databind/DeserializationContext.java:1575

     */
    public Object handleNullForPrimitives(Class<?> targetClass,
            JsonParser p, ValueDeserializer<?> deser,
            String msgTemplate, Object... msgArgs)
        throws JacksonException
    {
        // but if not handled, just throw exception
        LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
        String msg = _format(msgTemplate, msgArgs);
        while (h != null) {
            // Can bail out if it's handled
            Object instance = h.value().handleNullForPrimitives(this, targetClass, p, deser, msg);
            if (instance != DeserializationProblemHandler.NOT_HANDLED) {
                // Sanity check for broken handlers, otherwise nasty to debug:
                if (_isCompatible(targetClass, instance)) {
                    return instance;
                }
                // In case our problem handler providing incompatible value,
                throw new InvalidFormatException(_parser,
                        "`DeserializationProblemHandler.handleNullForPrimitives()` for type %s returned value of type %s".formatted(
                                ClassUtil.nameOf(targetClass), ClassUtil.getClassDescription(instance)),
                    instance, targetClass
                        );
            }
            h = h.next();
        }
        return reportInputMismatch(deser, msg);
    }
    /**
     * Method that deserializers should call if they fail to instantiate value
     * due to lack of viable instantiator (usually creator, that is, constructor
     * or static factory method). Method should be called at point where value
     * has not been decoded, so that handler has a chance to handle decoding
     * using alternate mechanism, and handle underlying content (possibly by
     * just skipping it) to keep input state valid
     *
     * @param instClass Type that was to be instantiated

View on GitHub (pinned to a50c7d2a1d)

Solutions

  1. In your handleNullForPrimitives(), branch on targetClass and return the correct boxed primitive type (Integer for int.class/Integer.class, Boolean for boolean, Double for double, etc.).
  2. Use the targetClass argument passed to the handler to produce a compatible value, and return DeserializationProblemHandler.NOT_HANDLED for types you don't specifically handle.
  3. Add a unit test that exercises the handler for every primitive type it might be called for.
  4. If you want a global default, prefer @JsonSetter(nulls=AS_DEFAULT) or DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_DEFAULT etc. rather than a broad handler.

Example fix

// before
@Override
public Object handleNullForPrimitives(DeserializationContext ctxt, Class<?> primitiveType,
        JsonParser p, ValueDeserializer<?> deser, String failureMsg) {
    return 0; // wrong for boolean/double!
}
// after
@Override
public Object handleNullForPrimitives(DeserializationContext ctxt, Class<?> primitiveType,
        JsonParser p, ValueDeserializer<?> deser, String failureMsg) {
    if (primitiveType == int.class || primitiveType == Integer.class) return Integer.valueOf(0);
    if (primitiveType == boolean.class || primitiveType == Boolean.class) return Boolean.FALSE;
    return NOT_HANDLED;
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate a handler's return type matches the primitive target before relying on it
static Object checkedNullDefault(Class<?> prim, Object v) {
    if (v == DeserializationProblemHandler.NOT_HANDLED) return v;
    if (v == null || !box(prim).isInstance(v)) {
        throw new IllegalStateException("handler returned incompatible " + v);
    }
    return v;
}
static Class<?> box(Class<?> p) {
    return java.lang.invoke.MethodHandles.reflectLookup(p); // pseudo; use a primitive-box map
}

Type guard

boolean compatibleWithPrimitive(Class<?> prim, Object v) {
    // crude check using a primitive->wrapper map
    return v != null && _isCompatible(prim, v);
}

Try / catch

try {
    return mapper.readValue(json, Bean.class);
} catch (InvalidFormatException e) {
    if (e.getMessage().contains("handleNullForPrimitives")) {
        // fix handler, do not blanket-ignore
    }
    throw e;
}

Prevention

When it happens

Trigger: A registered DeserializationProblemHandler.handleNullForPrimitives() returns, e.g., a String for an int field, a Long for a boolean, or null for a primitive that the handler claimed to handle; returning a boxed type that doesn't match (e.g. Double for a float field is fine, but Integer for a double is not, depending on widening rules).

Common situations: Writing a 'null to default' handler that returns a single sentinel object for all primitives; a handler returning a value computed from the wrong field/type after refactoring; 2.x handlers ported to 3.x where the primitive type passed in changed signature; handler returns 0 for int but is also invoked for boolean and returns the same 0 (boxing to Integer, incompatible with boolean).

Related errors


AI-assisted analysis of FasterXML/jackson-databind@a50c7d2a1d (2026-08-06). Data as JSON: /api/errors/69a081b17d10de67. Report an issue: GitHub.