Strange response returned by sttp

Hey all!

I was writing an integration test for one of my endpoints, using tapir, sttp and circe, when I ran into a weird response being returned.
My endpoint returns a Unit which I think should be decodable by circe, however when I make this call

val backend: SttpBackend[Identity, Any] = OkHttpSyncBackend()

val deleteBody = DeleteContentRequest(List(0L)).asJson.toString()

val deleteResponse = basicRequest
      .delete(uri"$externalBaseUri/libraries/30872/contents")
      .headers(contentType, csrfTokenHeader)
      .cookies(someCookies(Some(testAccount1)): _*)
      .body(deleteBody)
      .response(asJsonEither[ErrorResponse, Unit])
      .send(backend)

the response that I get is contains both Left(sttp.client3.DeserializationException: exhausted input) and a 200 which seems strange to me.

Response(Left(sttp.client3.DeserializationException: exhausted input),200,,List(content-length: 0, date: Wed, 24 Apr 2024 22:50:26 GMT, vary: Origin, access-control-allow-credentials: true),List(),RequestMetadata(DELETE,https://www.example.com/myDeleteEndpoint,Vector(Accept-Encoding: gzip, deflate, Content-type: application/json; charset=utf-8, X-CSRF-Token: //token, Cookie: //cookie, Content-Length: 29)))

I’m not quite sure what is going on here and was wondering if someone might have any insights? It feels like it could be a bug.

I think circe would decode only null, {} or [] to Unit, while in this case we are dealing with an empty response.
To make this work, such an endpoint could be defined in Tapir with following outputs:

.out(emptyOutput)
.errorOut(jsonBody[ErrorResponse])

and then your request should define a response like:

 .response(asEither(asJson[ErrorResponse], ignore))

to make this work.