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 instantiatedView on GitHub (pinned to a50c7d2a1d)
Solutions
- 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.).
- 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.
- Add a unit test that exercises the handler for every primitive type it might be called for.
- 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
- Branch on the target primitive class in your handler and return the matching wrapper.
- Return NOT_HANDLED for any primitive type your handler does not explicitly cover.
- Unit-test the handler against every primitive/wrapper type it might be invoked for.
- Prefer field-level @JsonSetter(nulls=AS_DEFAULT) or @JsonInclude defaults over a broad handler.
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
- Invalid abstract type resolution from {} to {}: latter is no
- Only support `JavaType` implementation of `ResolvedType`, no
- Failed to parse Date value '%s': %s
- maxProblems must be positive
- Cannot handle managed/back reference '{}': type: value deser
AI-assisted analysis of FasterXML/jackson-databind@a50c7d2a1d (2026-08-06).
Data as JSON: /api/errors/69a081b17d10de67.
Report an issue: GitHub.