FasterXML/jackson-databind · error · InvalidFormatException

`DeserializationProblemHandler.handleNullForPrimitives()`…

Error message

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

What it means

Thrown by DeserializationContext.handleNullForPrimitives() when a registered DeserializationProblemHandler returns a non-null value for a null-to-primitive coercion but that value's type is incompatible with the target primitive type. Jackson validates the handler's return value via _isCompatible() to catch buggy handlers early, since silent type mismatches on primitives would cause confusing downstream errors.

Solutions

  1. Ensure your handleNullForPrimitives() returns a value whose type matches or is a wrapper of the targetClass parameter (e.g., return Integer 0 for int targetClass).
  2. Return DeserializationProblemHandler.NOT_HANDLED if your handler cannot handle the specific case, letting other handlers or the default behavior take over.
  3. Add a type check in your handler: if (!targetClass.isInstance(myDefault)) return NOT_HANDLED; before returning.

Example fix

// before
@Override
public Object handleNullForPrimitives(DeserializationContext ctxt, Class<?> targetType,
        JsonParser p, ValueDeserializer<?> deser, String msg) {
    return "0"; // wrong: String is not compatible with int
}
// after
@Override
public Object handleNullForPrimitives(DeserializationContext ctxt, Class<?> targetType,
        JsonParser p, ValueDeserializer<?> deser, String msg) {
    if (targetType == int.class) return 0;
    return NOT_HANDLED;
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate handler return type before returning
@Override
public Object handleNullForPrimitives(DeserializationContext ctxt, Class<?> targetType,
        JsonParser p, ValueDeserializer<?> deser, String msg) {
    Object candidate = computeDefault(targetType);
    if (candidate != null && !targetType.isInstance(candidate)
        && !(targetType.isPrimitive() && ClassUtil.wrapperType(targetType).isInstance(candidate))) {
        return DeserializationProblemHandler.NOT_HANDLED; // let default handle it
    }
    return candidate;
}

Type guard

boolean isHandlerValueCompatible(Class<?> target, Object value) {
    if (value == null || target.isInstance(value)) return true;
    return target.isPrimitive() && ClassUtil.wrapperType(target).isInstance(value);
}

Prevention

When it happens

Trigger: Registering a custom DeserializationProblemHandler that overrides handleNullForPrimitives() and returns a value that is not assignable to the target primitive's wrapper type. For example, the handler returns a String "0" when the target is int, or returns a Long when the target is a double field (though wrapper compatibility is checked for primitives).

Common situations: Custom problem handlers designed to supply defaults for null primitives that return the wrong Java type. Handlers returning null explicitly (which is allowed). Handlers returning domain objects instead of primitive-compatible values.

Related errors


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

Appendix: 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 87876ca5c0)