Related to #645 and #648, it appears that code which uses MonadThrow and ApplicativeThrow (or their equivalent *Errors) for generic error handling cannot safely be composed with Handle.allow(F).
The issue appears to be that Handle.Submarine does not extend ControlThrowable so it's not filtered out by NonFatal and it'll be caught by any of the adapt or recover methods that handle errors generically instead of enumerating a subset of error types.
Here's a gist with an example of how a simple generic retry helper could produce this behavior: https://gist.github.com/morgen-peschke/c3c65a25bae244e4a1c6e83a387b661c
Output is:
$ scala-cli run Example.scala
Compiling project (Scala 2.13.15, JVM (21))
Compiled project (Scala 2.13.15, JVM (21))
============ Expecting OutOfRetries with nested OperationFailures ============
Retries exceeded after 2 attempts:
cats.mtl.Handle$Submarine
cats.mtl.Handle$Submarine
It's not visible in the output, but the expected OperationFailure instances are nested inside the Submarine instances.
Related to #645 and #648, it appears that code which uses
MonadThrowandApplicativeThrow(or their equivalent*Errors) for generic error handling cannot safely be composed withHandle.allow(F).The issue appears to be that
Handle.Submarinedoes not extendControlThrowableso it's not filtered out byNonFataland it'll be caught by any of the adapt or recover methods that handle errors generically instead of enumerating a subset of error types.Here's a gist with an example of how a simple generic retry helper could produce this behavior: https://gist.github.com/morgen-peschke/c3c65a25bae244e4a1c6e83a387b661c
Output is:
It's not visible in the output, but the expected
OperationFailureinstances are nested inside theSubmarineinstances.