Fixing Play Framework Json.format Errors When Upgrading to Scala 3
Understanding the Scala 3 Play JSON Macro Error
Upgrading a large codebase from Scala 2 to Scala 3 is a major milestone, but it often surfaces macro-related breaking changes. If you encounter the compiler error Fails to resolve field: 'net.group.rest.member.Country._1' when calling Json.format[Country] with Play Framework 3 (and Play JSON 3.0.x), you are not alone.
In Scala 2, Play JSON relied on Scala 2 reflection and whitebox macros to generate OFormat instances. In Scala 3, Play JSON was rewritten to use Scala 3's new macro system and compile-time Mirror derivation (specifically scala.deriving.Mirror.ProductOf). Because the underlying derivation engine changed, certain case class structures or missing implicits that compiled silently in Scala 2 now break during Scala 3 macro expansion.
Common Causes and How to Fix Them
1. Inner Case Classes and Enclosing Scopes
Scala 3's Mirror derivation requires macro-analyzed case classes to be directly accessible at the top level or within a plain single object. If your Country case class is defined inside a trait, class, or package object, the Scala 3 macro may fail to inspect its primary constructor fields, causing it to fall back to generic Product tuple representations (e.g., searching for _1, _2).
Solution: Move the case class out of inner traits or nested classes into top-level definitions or standard package objects: