2

I have created hierarchy of errors:

sealed trait MyError extends Throwable
final case class SecondError(msg: String) extends MyError

Now I could get this kind of error in my http4s routes:

case GET -> Root / "things" => for {
        response <- things.all.foldM(
          error => error match {
            case SecondError(_) => InternalServerError(error)            
          }
...

But I get compiled error:

could not find implicit value for parameter encoder: io.circe.Encoder[Throwable]

Is it possible to encode Throwable with circe and http4s? I tried to do it this way:

implicit def encoderHttpThrowable: EntityEncoder[Env, Throwable] = jsonEncoderOf[Env, Throwable]

But it did not solve a problem.

pme
  • 14,156
  • 3
  • 52
  • 95
Developus
  • 1,400
  • 2
  • 14
  • 50
  • From memory you don't have your `MyError` trait extend `Throwable` but instead match the `Throwable` to one of your `MyError`s. Then you just need to encode the error's case class – randbw Jan 16 '20 at 14:48
  • You mean extend Error instead of Throwable? What if functions I use need Throwable in signature? – Developus Jan 16 '20 at 14:49
  • At the "end of the world" you have a function of `Throwable => MyError` and then circe encodes the result of that – randbw Jan 16 '20 at 14:50
  • Here is an article which deals with this: https://typelevel.org/blog/2018/08/25/http4s-error-handling-mtl.html – randbw Jan 16 '20 at 14:52
  • I have encoders for my custom errors. The problem is only with Throwable – Developus Jan 16 '20 at 14:57
  • What type is `InternalServerError` exactly: `org.http4s.dsl.impl.InternalServerError`? – pme Jan 16 '20 at 18:50
  • Yes, it is clear Status code from http4s – Developus Jan 16 '20 at 18:52

1 Answers1

1

It is not possible to encode automatically a Java Hierarchy with Circe (or any other library) (I am pretty sure of this).

So you have 3 possibilities:

  1. Just use the Message of the error:

    case GET -> Root / "things"  =>
        things.all.foldM (
          error => InternalServerError(error.getMessage()),
          Ok(_)
        )
    
  2. Or get rid of the Throwable:

      sealed trait MyError
      final case class SecondError(msg: String) extends MyError
    

    And now you can encode the error

    ...
    case GET -> Root / "things"  =>
      things.all.foldM (
        error => InternalServerError(error),
        Ok(_)
      )
    
  3. Map your Throwable to your own Error (Sealed Trait without Throwable):

    ...
    case GET -> Root / "things"  =>
      things.all
        .mapError{
          case ex: IllegalArgumentException => SecondError(ex.getMessage)
          case ex => FirstError(ex.getMessage)
       }
        .foldM (
        error => InternalServerError(error),
        Ok(_)
      )
    
pme
  • 14,156
  • 3
  • 52
  • 95