quarkus-resteasy-problem
quarkus-resteasy-problem copied to clipboard
Stacktrace not printing out when enum fails to parse
Maybe I'm missing a setting but when I send in an invalid enum into my REST endpoint, I get the 400 Bad Request as expected. However, I don't get any other information about WHY it was a bad requesst in my logs.
I just get that it was a 400 bad request.
Is there a way to get the stack trace to print out on WHAT caused the bad request?
Thanks!
@tmulle got to the bottom of this.
-
its being caught by this mapper: https://github.com/quarkiverse/quarkus-resteasy-problem/blob/main/runtime/src/main/java/io/quarkiverse/resteasy/problem/jackson/InvalidFormatExceptionMapper.java
-
I temporarily stuck the stack trace in so you can see where its coming from...
at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1959)
at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:1245)
at com.fasterxml.jackson.databind.deser.std.EnumDeserializer._deserializeAltString(EnumDeserializer.java:447)
at com.fasterxml.jackson.databind.deser.std.EnumDeserializer._fromString(EnumDeserializer.java:304)
at com.fasterxml.jackson.databind.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:273)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:310)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2125)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1501)
at io.quarkus.resteasy.reactive.jackson.runtime.serialisers.ServerJacksonMessageBodyReader.doReadFrom(ServerJacksonMessageBodyReader.java:93)
at io.quarkus.resteasy.reactive.jackson.runtime.serialisers.ServerJacksonMessageBodyReader.readFrom(ServerJacksonMessageBodyReader.java:44)
at io.quarkus.resteasy.reactive.jackson.runtime.serialisers.ServerJacksonMessageBodyReader.readFrom(ServerJacksonMessageBodyReader.java:83)
at io.quarkus.resteasy.reactive.jackson.runtime.serialisers.ServerJacksonMessageBodyReader_ClientProxy.readFrom(Unknown Source)
at org.jboss.resteasy.reactive.server.handlers.RequestDeserializeHandler.readFrom(RequestDeserializeHandler.java:126)
at org.jboss.resteasy.reactive.server.handlers.RequestDeserializeHandler.handle(RequestDeserializeHandler.java:84)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:135)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$15.runWith(VertxCoreRecorder.java:638)
at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:1583)
As suspected this is happening in RestEasy when it tries to serialize the JSON before even getting into your code.
- So to solve this I think you just need to create your own Mapper for this exception that has a HIGHER priority and logs the exception.
@Priority(Priorities.USER)
public final class InvalidFormatExceptionMapper extends ExceptionMapperBase<InvalidFormatException> {
Logger log = Logger.getLogger(InvalidFormatExceptionMapper.class);
@Override
protected HttpProblem toProblem(InvalidFormatException exception) {
log.errorf("%s", exception.getOriginalMessage());
return HttpProblem.builder()
.withStatus(Response.Status.BAD_REQUEST.getStatusCode())
.withTitle(Response.Status.BAD_REQUEST.getReasonPhrase())
.withDetail(exception.getOriginalMessage())
.withContext("field", serializePath(exception.getPath()))
.build();
}
@melloware thanks for debugging this, it's actually a good news that it is caught by already existing mapper.
IMHO this particular case is still client side error sending wrong string value, and I think we should rather improve a detail of the problem (in this extension) telling api client exactly what's wrong rather than logging stack trace for server developers. What do you guys think?
So for me here is the HTTP problem that comes out when I have a ENUM that won't parse or is invalid.
{
"status": 400,
"title": "Bad Request",
"detail": "Cannot deserialize value of type `com.melloware.Announcement$AnnouncementType` from String \"WARN\": not one of the values accepted for Enum class: [IT, WEATHER, INFO, ALERT]",
"instance": "/api/v1/announcement",
"context": {
"field": "type",
"spanId": "d538c334a1d7ed53",
"traceId": "c8e1722bd2720735424bb9fce6e7c2a9"
}
}
So i think the problem is great i think @tmulle issue is this doesn't show in in your logs at all which can make production debugging difficult.
Maybe an output to LOG.error all HttpProblems created which of course would be false by default? @tmulle thoughts?
We should add a quality toString() method to HttpProblem if it doesn't already have one?
I still think the detail could he more end-user friendly, e.g. should not include enum class fully qualified name etc.
Regarding logs, looks like you have log level for ROOT or http-problem logger set higher than INFO, otherwise you would see this:
2025-04-03 14:32:08,555 INFO [http-problem] (executor-thread-1) status=400, title="Bad Request", detail="Cannot deserialize value of type `com.melloware.Announcement$AnnouncementType` from String \"WARN\": not one of the values accepted for Enum class: [IT, WEATHER, INFO, ALERT]"
4XX are logged as INFO, 5XX as errors (with stack trace included).
@tmulle yep I am seeing mine at INFO level? Can you check yours?
I get this during my test from curl.
I have 2 MDC variables being included also: X-Request-id and X-Request-Address
This is using the latest git pull from the main branch.
{
"status": 400,
"title": "Bad Request",
"detail": "Cannot deserialize value of type `org.acme.StatusEnum` from String \"BAD_VALUE\": not one of the values accepted for Enum class: [OK, FAILED]",
"instance": "/server/enumTest",
"field": "status",
"X-Request-Id": "71035ba9-b691-4084-aca8-c819bf5f2c91",
"X-Request-Address": "127.0.0.1"
}
and this in the quarkus console log:
2025-04-03 10:53:09,816 INFO [http-problem] (executor-thread-1) status=400, title="Bad Request", detail="Cannot deserialize value of type `org.acme.StatusEnum` from String "BAD_VALUE": not one of the values accepted for Enum class: [OK, FAILED]", field="status"
Safe to close this ticket @tmulle ?
@tmulle safe to close?
Yes I will close it since I tested that it now prints an error