quarkus-resteasy-problem icon indicating copy to clipboard operation
quarkus-resteasy-problem copied to clipboard

Stacktrace not printing out when enum fails to parse

Open tmulle opened this issue 1 year ago • 7 comments

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 avatar Jul 29 '24 12:07 tmulle

@tmulle got to the bottom of this.

  1. 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

  2. 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.

  1. 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 avatar Apr 03 '25 11:04 melloware

@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?

lwitkowski avatar Apr 03 '25 12:04 lwitkowski

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?

melloware avatar Apr 03 '25 12:04 melloware

We should add a quality toString() method to HttpProblem if it doesn't already have one?

melloware avatar Apr 03 '25 12:04 melloware

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).

lwitkowski avatar Apr 03 '25 12:04 lwitkowski

@tmulle yep I am seeing mine at INFO level? Can you check yours?

melloware avatar Apr 03 '25 13:04 melloware

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"

tmulle avatar Apr 03 '25 15:04 tmulle

Safe to close this ticket @tmulle ?

melloware avatar May 08 '25 01:05 melloware

@tmulle safe to close?

melloware avatar May 23 '25 00:05 melloware

Yes I will close it since I tested that it now prints an error

tmulle avatar May 23 '25 12:05 tmulle