Description
Using:
scala 2.13.16
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.35.3"
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.35.3" % Provided
(and previous versions of jsoniter-scala going back quite some time -- I don't think this is in anyway a recently-introduced issue)
When compiling the code generated by the tapir openapi generator (example output), I will occasionally hit an error looking as follows:
[error] /home/runner/work/myapp/target/scala-2.13/src_managed/main/sbt-openapi-codegen/TapirGeneratedEndpointsJsonSerdes.scala:12:186: Cannot evaluate a parameter of the 'make' macro call for type 'sttp.tapir.generated.myapp.TapirGeneratedEndpoints.Payload'. It should not depend on code from the same compilation module where the 'make' macro is called. Use a separated submodule of the project to compile all such dependencies before their usage for generation of codecs. Cause:
[error] java.lang.AssertionError: assertion failed: com package com <none>
[error] implicit lazy val payloadJsonCodec: com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[Payload] = com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker.make(com.github.plokhotnyuk.jsoniter_scala.macros.CodecMakerConfig.withAllowRecursiveTypes(true).withTransientEmpty(false).withTransientDefault(false).withRequireCollectionFields(true))
I have seen this happen on a relatively minimal file:
implicit def seqCodec[T: com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec]: com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[List[T]] =
com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker.make[List[T]]
implicit def optionCodec[T: com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec]: com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[Option[T]] =
com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker.make[Option[T]]
implicit lazy val payloadJsonCodec: com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[Payload] = com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker.make(com.github.plokhotnyuk.jsoniter_scala.macros.CodecMakerConfig.withAllowRecursiveTypes(true).withTransientEmpty(false).withTransientDefault(false).withRequireCollectionFields(true))
implicit lazy val otherModelJsonCodec: com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[OtherModel] = com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker.make(com.github.plokhotnyuk.jsoniter_scala.macros.CodecMakerConfig.withAllowRecursiveTypes(true).withTransientEmpty(false).withTransientDefault(false).withRequireCollectionFields(true))
implicit lazy val innerBodySchemaJsonCodec: com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[InnerBodySchema] = com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker.make(com.github.plokhotnyuk.jsoniter_scala.macros.CodecMakerConfig.withAllowRecursiveTypes(true).withTransientEmpty(false).withTransientDefault(false).withRequireCollectionFields(true))
implicit lazy val anotherModelJsonCodec: com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[AnotherModel] = com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker.make(com.github.plokhotnyuk.jsoniter_scala.macros.CodecMakerConfig.withAllowRecursiveTypes(true).withTransientEmpty(false).withTransientDefault(false).withRequireCollectionFields(true).withDiscriminatorFieldName(scala.None).withDiscriminatorFieldName(scala.None))
implicit lazy val anotherModel2JsonCodec: com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[AnotherModel2] = com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker.make(com.github.plokhotnyuk.jsoniter_scala.macros.CodecMakerConfig.withAllowRecursiveTypes(true).withTransientEmpty(false).withTransientDefault(false).withRequireCollectionFields(true).withDiscriminatorFieldName(scala.None).withDiscriminatorFieldName(scala.None))
implicit lazy val innerBodyCodec: com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[InnerBody] = new com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[InnerBody] {
def decodeValue(in: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, default: InnerBody): InnerBody = {
List(
innerBodySchemaJsonCodec)
.foldLeft(Option.empty[InnerBody]) {
case (Some(v), _) => Some(v)
case (None, next) =>
in.setMark()
scala.util.Try(next.asInstanceOf[com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[InnerBody]].decodeValue(in, default))
.fold(_ => { in.rollbackToMark(); None }, Some(_))
}.getOrElse(throw new RuntimeException("Unable to decode json to untagged ADT type InnerBody"))
}
def encodeValue(x: InnerBody, out: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): Unit = x match {
case x: InnerBodySchema => InnerBodySchemaJsonCodec.encodeValue(x, out)
}
def nullValue: InnerBody = InnerBodySchemaJsonCodec.nullValue
}
implicit lazy val messageJsonCodec: com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[Message] = com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker.make(com.github.plokhotnyuk.jsoniter_scala.macros.CodecMakerConfig.withAllowRecursiveTypes(true).withTransientEmpty(false).withTransientDefault(false).withRequireCollectionFields(true))
The relevant models seem to be
case class Payload (
body: InnerBody,
some: String,
other: String,
params: Seq[String]
)
sealed trait InnerBody
case class InnerBodySchema (
has: String,
some: Int,
more: Seq[DateTime],
params: String
) extends InnerBody
although I haven't been able to produce a minimal version that definitely triggers this yet, I'm afraid - in part because it happens so rarely. I can't even get the failure locally on the exact file that's caused it before. It seems to happen more often when building in CI docker for some mysterious reason 😅
Anyway I'm not sure if this is really a jsoniter-scala bug or a scala bug or what, but thought I'd raise it here in case @plokhotnyuk had any ideas.
Perhaps there might be some workaround we could do in the tapir codegen to ameliorate this flakiness?