Skip to content

Differentiate between input and code exceptions on deserialization #1356

@nickbabcock

Description

@nickbabcock

Summary of dropwizard discussion, specifically the following comment:

When a server receives a payload, attempts to deserialize it, and an exception is raised, there are two possibilities in our eyes:

  • A client sent unexpected input and should receive a 4xx response back
  • There is a programmatic error (forgot proper annotations, etc) and a 5xx response should be returned

The issue is that both of these situations may throw the same root exception JsonMappingException, which makes it hard to determine what response should be returned. I propose a new exception: JsonInputMappingException (name subject to change) or increase usage of InvalidFormatException

Examples

Examples that use just a plain JsonMappingException where new JsonInputMappingException or InvalidFormatException would be used

  • Expecting an object, but JSON is false throws a JsonMappingException of "Can not construct instance of foo: no boolean/Boolean-argument constructor/factory method to deserialize from boolean value (false)". This should be a client error because the wrong json type was submitted.
  • Expecting a list but given a string throws a JsonMappingException of "Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token"
  • Deserializing a DateTime from [1,1,1,1] throws a JsonMappingException of "Unexpected token (VALUE_NUMBER_INT), expected END_ARRAY: Expected array to end."

The Dream

The code needed to determine if it client or server issue:

final JsonMappingException ex = (JsonMappingException) exception;
final Throwable cause = Throwables.getRootCause(ex);

// Exceptions that denote an error on the client side
final boolean clientCause = cause instanceof InvalidFormatException ||
    cause instanceof PropertyBindingException ||
    cause instanceof JsonInputMappingException;

if (!clientCause) {
    LOGGER.error("Unable to serialize or deserialize the specific type", exception);
    return Response.serverError().build();
}

or maybe even:

final boolean clientCause = cause instanceof JsonInputMappingException

and have InvalidFormatException and PropertyBindingException extend from JsonInputMappingException, though I do not know if this would be 100% applicable.

Considerations

By creating a new exception that is supposed to represent bad input, exceptions thrown due to bad input in third party libraries that use plain JsonMappingException, if not changed, can be unexpectedly interpreted as a server/programmatic error. I believe it is best to err on the side of blaming the client/input. Instead, creating a JsonProgrammaticMappingException may fix this problem.

Would JsonParseException be a part of JsonInputMappingException

The suggested exception, JsonInputMappingException, does not apply to serialization. An error on serialization is almost certainly a programmatic error, though I can see a situation where a custom serializer throws on certain input. I understand this to be less of a use case.

Do not create a new exception JsonInputMappingException or JsonProgrammaticMappingException, but instead throw instances of InvalidFormatException for the given examples (more examples may exist, but I'm not aware of them at this point).

Conclusion

Let me know if you need help recreating any examples. I'm torn whether it's best to create a new exception or use InvalidFormatException more.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions