| 1 |
grails-web-databinding/.../GrailsWebDataBinder.groovy |
|| flow state inside a closure infers as void |
ArrayList#leftShift(void) |
Section 1 is fixed upstream |
App source if the app has the same @CompileStatic pattern. Framework bind behaviour unchanged |
| 2 |
grails-core/.../config/external/WriterFilteringMap.groovy |
@Delegate now generates mutator methods that were previously excluded |
WriteFilteringMapSpec: getWrittenValues().size() == 0 |
Groovy restores the previous @Delegate mutator exclusion |
App source if the app uses @Delegate the same way. Grails config filtering stays correct because of this change |
| 3 |
grails-datamapping-core/.../GormEntity.groovy |
Generic trait method signatures are specialized differently, so trait-injected merge(Object) is not found |
NoSuchMethodException: Book.merge(Object) |
Groovy restores prior generic trait-signature resolution |
App runtime. Domain merge() is a public GORM API. Without this, upgraded apps can fail at runtime when calling merge |
| 4 |
grails-datastore-core/.../reflect/ClassPropertyFetcher.java |
Interface methods surface differently during property introspection |
MissingMethodException ... __transients$get |
Unknown - needs upstream confirmation |
App runtime. Property / transients introspection is used by GORM and databinding. Apps should not call this class directly |
| 5 |
grails-testing-support-http-client/.../utils/XmlUtils.groovy |
SAX/JAXP feature set recognised by the default parser changed |
DOCTYPE is disallowed... |
Unknown. This is XXE-hardening; do not drop it for convenience |
App test build if the app uses this testing helper. Not a production runtime change. Keep the hardening |
| 6 |
grails-validation/.../ValidateableTraitSpec.groovy |
Static trait methods are emitted with a modifier combination the verifier rejects |
illegal combination of modifiers: abstract and static |
Groovy fixes static trait-method emission |
App source. Validateable is a public trait. Command objects / domain-like types in the app can fail class verification on Groovy 6 |
| 7 |
grails-data-hibernate7/.../HibernateGormInstanceApi.groovy |
Negated instanceof requires explicit parenthesization |
Compile failure |
Groovy restores prior parsing precedence |
App source if the app has !(x instanceof Foo) under static compile. Framework persistence behaviour unchanged |
| 8 |
grails-fields/.../BeanPropertyAccessorImpl.groovy |
@Canonical no longer implies @MapConstructor |
Compile failure |
Groovy reinstates implied @MapConstructor, or Grails keeps the explicit annotation permanently |
App source. Apps using @Canonical and expecting a map constructor must add @MapConstructor themselves on Groovy 6 |
| 9 |
grails-views-gson/.../GrailsJsonViewHelper.groovy, .../internal/TemplateRenderer.groovy |
Closure/generic inference under @CompileStatic |
Compile failure |
Unknown |
Framework only unless the app copies these helpers. Gson views keep compiling because of this |
| 10 |
grails-testing-support-http-client/.../HttpClientSupport.groovy |
A static final constant in a Spec collides with a trait instance getter |
cannot have both a static and an instance method |
Groovy restores prior static/instance resolution |
App test build. Specs that mix a static FORM (or similar) with this trait will fail to compile. Rename the constant or drop the static |
| 11 |
gradle/*-test-config.gradle, gradle.properties, CompilePlugin, forge template |
Spock 2.4 is built against Groovy 5 and refuses to run on Groovy 6 |
IncompatibleGroovyVersionException / SpockTransform |
Spock ships 2.4-groovy-6.0 (not on Maven Central as of 2026-08-18; latest is 2.4-groovy-5.0. Spock master compiles against Groovy 6.0.0-alpha-2 only) |
App test build. Any app or plugin that compiles/tests with Spock on Groovy 6 must pass -Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=true until Spock publishes a groovy-6 artifact. This is the largest end-user item |
| 12 |
grails-common/build.gradle, dependencies.gradle |
groovy-callsite is a separate module in Groovy 6 |
Missing class at runtime when indy is off |
Never |
Permanent. Apps/plugins that compile with indy = false need groovy-callsite on the compile/runtime classpath |
| 13 |
build-logic/.../SbomPlugin.groovy |
Groovy 6 pulls JLine 4, whose POM omits the licence SBOM validation expects |
SBOM licence validation failure |
JLine publishes complete licence metadata |
Framework only (Grails repo / SBOM). Not an application concern |
| 14 |
settings.gradle |
Micronaut island is pinned to Groovy 5 |
Version conflict if included |
The Micronaut island supports Groovy 6 (#16161) |
App runtime / BOM. Groovy 6 snapshots omit grails-micronaut-*. Apps that depend on Micronaut integration cannot use those artifacts from this line yet |
| 15 |
.github/workflows/groovy-joint-workflow.yml |
Groovy 6 development is still on master; GROOVY_6_0_X does not exist |
Joint validation checks out a missing branch |
Apache Groovy cuts GROOVY_6_0_X |
Framework only (Grails CI) |
Tracking issue for the changes Apache Grails needs in order to build, test, and ship against Apache Groovy 6. It documents what landed via #15558 on
9.0.x, why each item exists, how it affects applications upgrading from Grails 7/8, and the condition under which it can be deleted.Current state: Groovy
6.0.0-beta-2, Grails 9 (9.0.x), JDK 21, Spring Boot 4.1, Spock2.4-groovy-5.0(nogroovy-6.0artifact published yet).Every row was established empirically: a workaround was only kept when removing it produced an observed failure, and only removed when the build and tests stayed green without it.
Documentation standard (treat every workaround the same)
Each remaining workaround must have all five of:
#16157).Do not leave "framework-only" items uncommented while test-only or build-only items are documented, or the reverse.
Impact classes
1. Upstream Groovy 6 regression (not filed upstream)
Static type checking merges the flow state of a
||inside a closure tovoid, so a variable guarded byx == null || ...becomes unusable in the branch body.The Map form fails equivalently with
Cannot find matching method java.util.LinkedHashMap#putAt(java.lang.Object, void).5.0.8; fails on6.0.0-beta-2.&&in the same position compiles. Only||triggers it.booleanlocal. Short-circuiting and null handling are unchanged.Applied in
grails-web-databinding/.../GrailsWebDataBinder.groovy(Collection and Map branches).Upgrade impact: App source. Any
@CompileStatic/@GrailsCompileStaticclosure in an application that uses||as a type guard can fail to compile on Groovy 6 until hoisted the same way. The Grails framework change does not alter runtime bind behaviour.2. Remaining items on 6.0.0-beta-2
grails-web-databinding/.../GrailsWebDataBinder.groovy||flow state inside a closure infers asvoidArrayList#leftShift(void)@CompileStaticpattern. Framework bind behaviour unchangedgrails-core/.../config/external/WriterFilteringMap.groovy@Delegatenow generates mutator methods that were previously excludedWriteFilteringMapSpec:getWrittenValues().size() == 0@Delegatemutator exclusion@Delegatethe same way. Grails config filtering stays correct because of this changegrails-datamapping-core/.../GormEntity.groovymerge(Object)is not foundNoSuchMethodException: Book.merge(Object)merge()is a public GORM API. Without this, upgraded apps can fail at runtime when callingmergegrails-datastore-core/.../reflect/ClassPropertyFetcher.javaMissingMethodException ... __transients$getgrails-testing-support-http-client/.../utils/XmlUtils.groovyDOCTYPE is disallowed...grails-validation/.../ValidateableTraitSpec.groovyillegal combination of modifiers: abstract and staticValidateableis a public trait. Command objects / domain-like types in the app can fail class verification on Groovy 6grails-data-hibernate7/.../HibernateGormInstanceApi.groovyinstanceofrequires explicit parenthesization!(x instanceof Foo)under static compile. Framework persistence behaviour unchangedgrails-fields/.../BeanPropertyAccessorImpl.groovy@Canonicalno longer implies@MapConstructor@MapConstructor, or Grails keeps the explicit annotation permanently@Canonicaland expecting a map constructor must add@MapConstructorthemselves on Groovy 6grails-views-gson/.../GrailsJsonViewHelper.groovy,.../internal/TemplateRenderer.groovy@CompileStaticgrails-testing-support-http-client/.../HttpClientSupport.groovystatic finalconstant in a Spec collides with a trait instance gettercannot have both a static and an instance methodFORM(or similar) with this trait will fail to compile. Rename the constant or drop the staticgradle/*-test-config.gradle,gradle.properties,CompilePlugin, forge templateIncompatibleGroovyVersionException/SpockTransform2.4-groovy-6.0(not on Maven Central as of 2026-08-18; latest is2.4-groovy-5.0. Spock master compiles against Groovy6.0.0-alpha-2only)-Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=trueuntil Spock publishes a groovy-6 artifact. This is the largest end-user itemgrails-common/build.gradle,dependencies.gradlegroovy-callsiteis a separate module in Groovy 6indy = falseneedgroovy-callsiteon the compile/runtime classpathbuild-logic/.../SbomPlugin.groovysettings.gradlegrails-micronaut-*. Apps that depend on Micronaut integration cannot use those artifacts from this line yet.github/workflows/groovy-joint-workflow.ymlmaster;GROOVY_6_0_Xdoes not existGROOVY_6_0_X3. Upgrade checklist for application authors (7.x / 8.x -> 9 on Groovy 6)
These are the items an application actually has to do or know. Everything else in the table is absorbed inside Grails.
org.spockframework:spock-core:2.4-groovy-6.0exists, add the version-check opt-out on Groovy compile and test JVMs (-Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=true). Without it,grails-test-coreand any Spec will fail to compile. This is unsupported by Spock and may produce odd runtime errors.groovy-callsite. If the app keepsgrails { indy = false }(the current plugin default), addorg.apache.groovy:groovy-callsiteor inherit it from the Grails BOM.@CompileStatic||guards inside closures. Hoist to abooleanlocal. Same as item 1 in the table.Validateable/ static trait methods. If verification fails withabstract and static, that is Groovy 6 trait emission, not a Grails API break.@Canonicalmap constructors. Add@MapConstructorexplicitly.!(x instanceof T)under static compile: parenthesize!(x instanceof T).grails-micronaut-bomand the island are not in Groovy 6 snapshots (#16161).GORM
merge(), transients introspection, and config filtering are handled inside Grails. Upgraded apps that stay on public APIs should not need source edits for those.4. Workarounds retired at 6.0.0-beta-2
9.6.1bumpCoreGrailsPluginBeanConfiguration.addPropertybeanRegistrarrewriteGrailsApplicationLifeCyclerewritten as a Java interface-PgrailsIndy=false6.0.0-beta-25. Not Groovy 6
ConfigurationBuildernested-map handling is Spring 7 and lives on8.0.xvia #16160 / #16159. It is not a Groovy 6 workaround.Verification
#15558 merged to
9.0.xon 2026-08-18. Snapshot publish is the9.0.xpush CIpublishjob.